Beluga

Members
  • Content Count

    478
  • Joined

  • Last visited

  • Days Won

    4

Beluga last won the day on September 30

Beluga had the most liked content!

Community Reputation

385 Excellent

About Beluga

  • Rank
    Sr. Member

Recent Profile Visitors

7,269 profile views
  1. I just now went looking in their issue trackers and found this in their enterprise support tracker (in category tab "Standard Feature Requests): AG-1202 Allow rendering rows dynamically adapting their height to their content That does sound like what I want, but thanks to their closed system, we have no way of knowing the exact contents of the issue! There is also this, which would only be for paying customers: Enterprise Row Model AG-1039 Allow dynamic row heights and maxBlocksInCache This is in Parked category: AG-2228 Allow lazy loading of rows when using autoHeight, ie on scroll to configured amount of rows, append n more rows at the bottom So looks like this stuff is on their radar (which I would have assumed based on how they acknowledge the pain point in their docs). Let's wait and see and for now enjoy my hack All this hacking does have the effect of improving my self-confidence and wanting to learn JS more deeply
  2. Earlier in this topic I asked about autoHeight for rows. Now I have created a nice and only slightly hacky solution for it (no hacks in libs, just working around stuff). The existing simple option is not acceptable in our use case, because even the official docs say: When using autoHeight for 35k rows, I got twice the message "the web page seems to be running slow, do you want to stop it". I don't even dare to imagine what would happen on an old smartphone. The obvious (to me) solution was to calculate automatic height on demand only for the handful of rows displayed at a time. This has to happen on page load, on filtering and on pagination navigation. Due how pagination is implemented, a hoop had to be jumped through with it as well (to avoid an infinite loop). To be clear: the ag-Grid API offered no immediately useful event I could listen to! viewportChanged sounded like it would work, but in practice it failed to cover page navigation. var paginationAttached = false; var paginationHandler = function (event) { rowHeighter(grid, event); } function rowHeighter(grid, event) { // calculate row height for displayed rows to wrap text var cols = [ grid.gridOptions.columnApi.getColumn("code"), grid.gridOptions.columnApi.getColumn("variation") ]; grid.gridOptions.api.getRenderedNodes().forEach(function(node) { node.columnController.isAutoRowHeightActive = function() { return true; }; node.columnController.getAllAutoRowHeightCols = function() { return cols; }; node.setRowHeight(grid.gridOptions.api.gridOptionsWrapper.getRowHeightForNode(node)); }); // if listening to paginationChange, onRowHeightChanged creates an infinite loop, so work around it if(paginationAttached === false) { grid.gridOptions.api.onRowHeightChanged(); grid.gridOptions.api.addEventListener('paginationChanged', paginationHandler); paginationAttached = true; } else { grid.gridOptions.api.removeEventListener('paginationChanged', paginationHandler); grid.gridOptions.api.onRowHeightChanged(); grid.gridOptions.api.addEventListener('paginationChanged', paginationHandler); } } You can see here that I had to brute-force overwrite a couple of functions in the ag-Grid row height calculation logic. I define the columns I want to target for the auto height and trick the getAllAutoRowHeightCols to just provide them. My filter event listener in RockGridItemAfterInit is grid.gridOptions.api.addEventListener('filterChanged', function(event) { rowHeighter(grid, event); }); A CSS rule is also required: div[col-id="variation"].ag-cell, div[col-id="code"].ag-cell { white-space: normal; } Using automatic row heights brought about an issue with the default fixed height grid: a wild scrollbar appeared! I found out I can make the grid height adapt, putting this into RockGridItemBeforeInit: // pagination with a fixed number of rows and adapting grid height grid.gridOptions.paginationAutoPageSize = false; grid.gridOptions.paginationPageSize = 15; grid.gridOptions.domLayout = 'autoHeight'; Then I decided the pagination controls should live above the grid, because otherwise their position will change annoyingly whenever the grid height changes. Putting this into RockGridItemAfterInit: // move the pagination panel to the top, because the dynamic row heights would change its position at the bottom var pagingPanel = document.querySelector('.ag-paging-panel'); var rootWrapper = document.querySelector('.ag-root-wrapper'); rootWrapper.prepend(pagingPanel);
  3. Beluga

    GD supports webp: http://php.net/manual/en/image.installation.php
  4. Beluga

    Firefox 65 will support WebP. Will be out in January 2019.
  5. Beluga

    Another possibility: https://wodby.com/docs/stacks/php/local/ I use one of their containers for my more exotic Docker setup. I like to keep my PW files (and MariaDB database) on my host so everything is clearer.
  6. Beluga

    Great, then installing gutenprint package should be enough! http://gimp-print.sourceforge.net/p_Supported_Printers.php
  7. Beluga

    Can you reveal the model? I have wrestled with a couple of Canons. One was easy as just installing gutenprint package did the trick. Another printer-related annoyance in Manjaro is that the user is not in "sys" group by default and thus is prevented access to fiddling with the printer in the CUPS admin. This does the trick: usermod -a -G sys yourusername
  8. Beluga

    I use Arch Linux myself and have installed Manjaro for various family members & friends. KDE for desktop environment. Even though Arch/Manjaro is the bleeding edge, breakage is rare. Most problems are related to printers and that is just a fact of life no matter which operating system we use. I use Arch for a couple of servers as well.
  9. I am using ApexCharts.js, which is kind of a spiritual successor to Chartist.js in that they both produce SVG charts. I am experimenting with what is possible, trying to figure out visualisations of the data that would be useful and attractive. Here is a screenshot of ApexCharts playing together in real time with RockGrid filtering (edit: nevermind the incorrect/repeating data labels, I only noticed and corrected later): We see a stacked bar chart representation of the number of literature references per filtered proverb type. I intend to split the thing into separate charts for each of the 13 top level categories (ApexCharts unfortunately does not support multiple series of stacked bars in a single chart). This will make it readable even with the unfiltered view of all 325 proverb types. It was quite convoluted to get the libraries to play together - grid.gridOptions.api.getModel().rootNode.childrenAfterFilter did not want to yield its contents, but guarded it like a jealous dragon. To get access to the data, I had to brute-force dispatch an input event like so: var inputTarget = document.querySelector('.ag-floating-filter-full-body:first-child input'); var inputEvent = new Event('input', {'bubbles': true, 'cancelable': true}); // have to use delta timing to delay the input event - in // case of big existing CPU load, it will fire too soon! var start = new Date().getTime(); setTimeout(function() { var now = new Date().getTime(), delta = now-start; inputTarget.dispatchEvent(inputEvent); },500); Then, to initialise the Apex chart in proper order, I had to wrap its stuff into a function. I called the function from my afterFilter: function afterFilter(grid) { var filterKids = grid.gridOptions.api.getModel().rootNode.childrenAfterFilter; var mapCodes = filterKids.map(x => x.data.code); var countedCodes = mapCodes.reduce((r,k)=>{r[k]=1+r[k]||1;return r},{}); apexseries = Object.entries(countedCodes).map(([p, v]) => ({'name':p, 'data':[v]})); var apexdiv = document.querySelector("#chart"); if(!apexdiv.hasChildNodes()) { apexi(); } else { ApexCharts.exec('proverbs', 'updateSeries', apexseries); } }
  10. Thanks for the free support I got it working with the syntax grid.gridOptions.api.addEventListener('filterChanged', function() { afterFilter() }); This allows me to pass the grid object to afterFilter and then do interesting stuff with grid.gridOptions.api.getModel().rootNode.childrenAfterFilter I am going to mess around with dynamic charting!!
  11. I am having trouble with events. In my RockGridItemAfterInit block I have grid.gridOptions.onFilterChanged = afterFilter(); Then outside it the function function afterFilter() { console.info("filter changed"); } The function fires exactly once - when the grid is initialised. It does not fire when the filters are changed. If I instead use grid.gridOptions.api.addEventListener('filterChanged', afterFilter()); It fires when the grid is initialised and when I change a filter, I get this in the console (the first time, on further tries I get nothing): TypeError: t is not a function ag-grid.min.js:26:2395 p</e.prototype.dispatchToListeners/</< http://0.0.0.0/site/modules/FieldtypeRockGrid/lib/ag-grid.min.js:26:2395 p</e.prototype.flushAsyncQueue/< http://0.0.0.0/site/modules/FieldtypeRockGrid/lib/ag-grid.min.js:26:2814 forEach self-hosted:262:13 p</e.prototype.flushAsyncQueue http://0.0.0.0/site/modules/FieldtypeRockGrid/lib/ag-grid.min.js:26:2785 <anonymous> self-hosted:973:17 What am I doing wrong?
  12. Not really, so I guess my solution is perfect for my case
  13. Those kinds that I can't think of before I hear them
  14. Here is how I do linkifying, any tips for improvements or alternatives welcome: In rockgrid.php I include 'name' as a column, but then in RockGridItemAfterInit I do grid.setColumns(['code', 'variation']); to make it hidden. I have these to get the urls I want: var url = document.URL; // URL API's origin gets us a href of the hostname without the trailing slash (does not work with IE11) var domain = new URL(url).origin; For the 'variation' column I have a cellRenderer to link to the children of the page using the 'name' column data: col.cellRenderer = function(params) { if(params.value !== null) { return '<a href="' + url + params.data.name + '">' + params.value + '</a>'; } } For the 'code' column I have a valueGetter to pull the human-readable name for the category from a JS object and stick it after the code. Then I use a cellRenderer to link the text to anywhere I want: // let's combine the code with its meaning col.valueGetter = function(params) { return params.data.code + ' ' + codesarray[params.data.code]; } col.cellRenderer = function(params) { return '<a href="' + domain + '/clas2/">' + params.value + '</a>'; }
  15. Screenshot demo of the external filter code in my comment: