UI frameworks and dynamic layouts based on breakpoints

All this React and Blaze 2.0 talk has been getting me excited to refactor my UI.

One thing that has frustrated me with the iron router and flow router layout engines is that they don’t seem particularly compatible with changing layouts based on responsive breakpoints in the application. It is also unclear to me how well Meteor and the routers work with alternative layout and component engines.

@dandv has put some nice work into integrating Webix which has nice layout features and some of the best widgets but it doesn’t seem like the layouts would be easy to change on the fly.

I’ve seen sematic-UI recommended as well but really back to fairly simple layout capabilities.

Any suggestions on layout management approaches that are compatible with reactive meteor and/or React and allow for dynamically applying different layouts based on screen size?

1 Like

We’ve been busy refactoring a half-dozen apps and dealing with the same issue(s). The general approach that we’ve been using is:

You can find all the work we’ve doing on Card UI in the ActiveLayout package.

API is still rather basic, but can be found at:
http://clinical-docs.meteor.com/#ActiveLayout

We’re working on a suite of verification and validation tests, but that probably won’t be finalized till April or May for this particular package.

But yeah, with all those pieces in place, we’ve gotten a lot of the prep work done for migrating to the JSS approach that React uses. And React feels a lot less drastically different. We’re looking to use the Sideburns/Blaze-React package as a stepping stone, and then an optimized rewrite.

3 Likes

These look like really good tips. Always good to get advice from someone that has been with Meteor for a long time. Really starting to feel like React is winning the mindshare of a lot of good Meteor developers.

Do you agree with component approach described in the Meteor guide for pure, re-useable, and smart components? or do you anticipate structuring your React code differently?

Mmmmm… I still have a lot of reservations about React and think it’s still half-baked. And, for what we’re wanting to do, we’ll be doing quite a few things differently with components.

1 Like

The easiest way to do this in React is to get the viewport width in the render method on the root page used by the router. I tend to store these in a ‘pages’ folder. Here’s an example using 1.3 syntax:

import AboutPageDesktop from '../pages/about/desktop'
import AboutPageMobile from '../pages/about/mobile'

const AboutPage = React.createClass({
  render() {
    const width = getViewportWidth();
    let AboutPage;

    if (width > 700) {
      AboutPage = AboutPageDesktop;
    } else {
      AboutPage = AboutPageMobile;
    }
    return <AboutPage />;
  }
});
export default AboutPage;

If you have to re-render on re-size you can use an event to fire (with debounce) a change to the state when the user is re-sizing… however it’s rare real users do this. In this case you would use setState somewhere above (or in Redux) and save the current width. Any changes will trigger a re-render.

2 Likes

well, Flow-router itself does not have anything to do with layout itself
but you can pass any arguments you want to BlazeLayout or ReactLayout inside action function/property
And also layout itself can render various things based on various things, for example screen size

And as a bonus old Abigail’s helper
https://github.com/awatson1978/meteor-cookbook/blob/master/cookbook/window.resize.md

I ended up using some of the advice from @awatson1978 . Slightly modified the pattern outlined for setting the width/height Session variables by throttling it among other things.

  $window = $(window)
  Session.setDefault("resize", null);
  Session.setDefault("appHeight", $window.height())
  Session.setDefault("appWidth", $window.width())
  $window.on "resize", _.throttle(->
    Session.set("resize", new Date())
    Session.set("appHeight", $window.height())
    Session.set("appWidth", $window.width())
  , 300, {leading: false})

I’m using Blaze with Iron-Router so technically I could reactively change the layout on re-size but this wouldn’t work very well in Flow-Router (which I plan on transition to). Instead I created a simple wrapping templates where I need to render different templates based on screen-size. Handling resize in React might need a different touch as @SkinnyGeek1010 mentioned. I could see getting the Session variable in getMeteorData, or handling it in a smarter parent component.

Using my header as an example:

In the router:

action: ->
   @render("header_layout", {to: "header_layout"})
   @render("content")

The template for the header yield:

<template name="header_layout">
  {{#if mobileHeader}}
  	{{> header_mobile}}
  {{else}}
  	{{> header}}
  {{/if}}
</template>

mobileHeader is a helper based on the appWidth Session variable

Personally I believe that bridging content presentation from desktop to mobile can often be handled with the right CSS approach, but the wheels start to fall off when I want the UX to change significantly between the two. I prefer to err on the side of duplicating presentation template code into two different screen specific views, rather than try to make a monolithic view that effectively handles all the UX differences at different screen sizes.

Outside of that core debate, anyone see any significant issues with my approach?

2 Likes

Hey @awatson1978,

I’ve been working with using the appWidth in my code and ran into an additional consideration that might be relevant to this discussion and your helper. $(window).width() does not include the width of the scrollbar so if there is a scrollbar any css media query breakpoints will not match up with your javascript breakpoints/width.

This answer on stackoverflow worked nicely for me:

Has anyone used something like Golden-Layout

@dandv I know you put some time into integration with Webix. Are you still using that approach and/or any thoughts on pros/cons?

My application is fairly desktop focused and would benefit quite a bit from being able to render widgets into different resizable columns, but that doesn’t seem like commonly tread ground.

What’s the reasoning behind using inline styles over CSS classes? Isn’t this generally discouraged for performance :confused:?

Webix layouts are super flexible and resize automatically.

You might also want to look at Isotope for a columns-based dynamic layout.

Did you run into any strong downsides using webix and/or is there a reason you have stopped work on the meteor integration effort?

The layout and resizer bits are in fact the pieces I’m most interested in and why I thought golden-layout with it’s MIT license might be a good alternative to webix.

I had seen Isotope but I guess hadn’t really considered it as the central layout control tool unless bin-packing was neccessary.

I mainly stopped working on the integration due to lack of time. While Webix does have its shortcomings, it’s gotten better over time, albeit slowly.

The main problems with Webix I see are:

  1. Their English is terrible, which undermines the credibility of the library, despite it being of very high quality
  • the documentation is sometimes just painful to follow
  • the blog posts are sometimes laughable
  1. Marketing is not their strongest suit, which means very few people from outside the Russian developer community find out about the framework, and even fewer (2) contribute, despite it being open source
  2. They seem quite understaffed for how powerful the library is, though I did see new folks in the team since 2014
  3. The library is monolithic (one 800kb or so un-minified .js), which makes it daunting to contribute. I’ve heard they do have it split up into modules internally, but they publish on GitHub only the built file.
  4. The code style is again unprofessional: random spacing, inconsistent quoting etc.
  5. They’re slow to respond (if at all), and many efforts are abandoned (e.g. the webix-firebase adapter), or documentation questions are left unanswered
  6. All these issues stem from a lack of strategic/business thinking. Hiring an editor to fix the English in the docs and make the blog posts professional wouldn’t be that expensive. Picking the low hanging fruit issues on GitHub would show activity and attract contributors.

Hey Dan,

Thanks for the detailed response. It’s good to know the issues you ran into were not some fundamental incompatibility with the way Meteor does things, which was my biggest concern.

I’ve been reading some of the docs and you are right that that isn’t their strong point. Was sort of toying with the idea of trying Webix with Apollo as the data stack to get some practical experience with both.