angular / flex-layout

Provides HTML UI layout for Angular applications; using Flexbox and a Responsive API
MIT License
5.9k stars 773 forks source link

Angular Layout Migration Guides #1426

Open CaerusKaru opened 1 year ago

CaerusKaru commented 1 year ago

In accordance with the announcement in the Medium post about this library's future, this is a tracking issue for requesting assistance in migrating complex usages of Angular Layout.

If you believe that your library falls under this case, you can request (or vote on existing) migration guides to other, supported solutions like Tailwind or Angular CDK.

Please use the +1 reaction wherever applicable, and do not comment +1. Those comments will be ignored/deleted.

I appreciate your patience as we go through this process. Until the campaign is complete, the library will still be actively maintained.

In your request, please include the following details:

This will help us prioritize the guides as they are submitted.

coreConvention commented 1 year ago

We use it in an extremely complex json-schema/ui-schema/formly reactive forms configuration based questionnaire-like form generator because it allows me to individually define things in a single ui-schema. For example:

Json-Schema

{  
  type: "object",
  properties: {
    someproperty: {
      type: "string"
    }
  }   
}

Ui-schema:

 {
   scope: "#",
   formlyConfig: {
     templateOptions: {
       fxLayout: "row wrap
     }     
   }
 }
 {
   scope: "#/properties/someproperty",
   formlyConfig: {
     templateOptions: {
       fxFlex: 50,
       fxLayoutAlign="end center"
     }     
   }
 }

I removed a lot of stuff for the example but you can hopefully see the correlation, I think. Alot of the above stuff is programatically generated from configuration in a mongo database, using a very complex json merging scheme.

Which then applies the necessary css to both the element generated by formly as well as the wrappers formly generates by adding the directives in the formly custom field types:

<div
  [fxLayout]="to.fxLayout ?? 'row wrap'"
  [fxLayoutAlign]="to.fxLayoutAlign ?? ''"
  [fxLayoutGap]="to.fxLayoutGap ?? '1rem grid'"
>
  <ng-container *ngFor="let f of field.fieldGroup; trackBy: trackById">
    <formly-field [fxFlex]="getFlex(f)" [field]="f"></formly-field>
  </ng-container>
</div>

Without this it would be almost impossible because some of the wrappers/parent elements formly generates can't be affected directly without parsing and validating existing classes applied to the elements. And doing some fancy pseduo class css magic that defeats the purpose of being able to apply it with just the json.

This is a bit of an oversimplification but without flex-layout this is going to be EXTREMELY difficult. Not all of the customizations can just be applied via css classes, as both formly and flex-layout use both the class and style attributes on the fly to do their magic and deal with the outer and inner elements where they are applied. Applying the flex css itself wouldn't be super difficult, its what the directives themselves do that makes this work, not just the css.

@DuncanFaulkner I would likely be willing to chip in on maintenance as well. As you can see from the above explanation I have a vested interest in seeing this project continue. I will end up having to write my own directives otherwise, which I will do if I have to, but this is such a great package I would rather continue using it lol!

jpike88 commented 1 year ago

I’d recommend keeping the goals nice and tight, aka maintenance mode. Only when things break in a way that’s obviously a break should it be put up as a bug fix. People here don’t need flex layout to do anything novel or different from its intended purpose, which should make taking it on very light work

PeterNSkovgaard commented 1 year ago

@DuncanFaulkner if you manage to keep this library up to speed with angular versions you are an absolute champ. As a very small startup we are using it widely and do not have the resources for the migration options suggested by the Angular team, so this would be a lifesaver.

Unfortunately we do not have the economy to fund it at the moment, but you will definitely have our appreciation! Hope it works out.

aceArt-GmbH commented 1 year ago

We replaced ~2000 simple usages of flex-layout with following CSS. Kinda hacky, but seems to work.

CSS ```scss // fxFlex [fxFlex] { flex: 1 1 0%; box-sizing: border-box; } *:has(> [fxFlex]) { flex-direction: row; box-sizing: border-box; display: flex; } [fxFlex='5'] { flex: 1 1 100%; box-sizing: border-box; max-width: 5%; } [fxFlex='20'] { flex: 1 1 100%; box-sizing: border-box; max-width: 20%; } [fxFlex='25'] { flex: 1 1 100%; box-sizing: border-box; max-width: 25%; } [fxFlex='30'] { flex: 1 1 100%; box-sizing: border-box; max-width: 30%; } [fxFlex='33'] { flex: 1 1 100%; box-sizing: border-box; max-width: 33%; } [fxFlex='50'] { flex: 1 1 100%; box-sizing: border-box; max-width: 50%; } [fxFlex='66'] { flex: 1 1 100%; box-sizing: border-box; max-width: 66%; } [fxFlex='67'] { flex: 1 1 100%; box-sizing: border-box; max-width: 67%; } [fxFlex='70'] { flex: 1 1 100%; box-sizing: border-box; max-width: 70%; } [fxFlex='80'] { flex: 1 1 100%; box-sizing: border-box; max-width: 80%; } [fxFlex='100'] { flex: 1 1 100%; box-sizing: border-box; max-width: 100%; } [fxFlex='1 0 auto'] { flex: 1 0 auto; box-sizing: border-box; } [fxFlex='0 1 auto'] { flex: 0 1 auto; box-sizing: border-box; } // fxLayout [fxLayout] { box-sizing: border-box; display: flex !important; } [fxLayout='row wrap'] { flex-flow: row wrap; flex: 1 1 1e-9px; } [fxLayout='row'] { flex-direction: row; } [fxLayout='column'] { flex-direction: column !important; &[fxLayoutGap='8px'] > *:not(:last-child) { margin-right: unset; margin-bottom: 8px; } &[fxLayoutGap='12px'] > *:not(:last-child) { margin-right: unset; margin-bottom: 12px; } &[fxLayoutGap='16px'] > *:not(:last-child) { margin-right: unset; margin-bottom: 16px; } &[fxLayoutGap='24px'] > *:not(:last-child) { margin-right: unset; margin-bottom: 24px; } &[fxLayoutGap='32px'] > *:not(:last-child) { margin-right: unset; margin-bottom: 32px; } & > [fxFlex='5'] { max-height: 5%; max-width: unset; } & > [fxFlex='20'] { max-height: 20%; max-width: unset; } & > [fxFlex='25'] { max-height: 25%; max-width: unset; } & > [fxFlex='30'] { max-height: 30%; max-width: unset; } & > [fxFlex='33'] { max-height: 33%; max-width: unset; } & > [fxFlex='50'] { max-height: 50%; max-width: unset; } & > [fxFlex='66'] { max-height: 66%; max-width: unset; } & > [fxFlex='67'] { max-height: 67%; max-width: unset; } & > [fxFlex='70'] { max-height: 70%; max-width: unset; } & > [fxFlex='80'] { max-height: 80%; max-width: unset; } & > [fxFlex='100'] { max-height: 100%; max-width: unset; } } // fxLayoutGap [fxLayoutGap='8px'] > *:not(:last-child) { margin-right: 8px; } [fxLayoutGap='12px'] > *:not(:last-child) { margin-right: 12px; } [fxLayoutGap='16px'] > *:not(:last-child) { margin-right: 16px; } [fxLayoutGap='24px'] > *:not(:last-child) { margin-right: 24px; } [fxLayoutGap='32px'] > *:not(:last-child) { margin-right: 32px; } // fxLayoutAlign [fxLayoutAlign] { display: flex; box-sizing: border-box; flex-direction: row; } [fxLayoutAlign='center center'] { place-content: center; align-items: center; } [fxLayoutAlign='end center'] { place-content: center flex-end; align-items: center; } [fxLayoutAlign='end top'] { place-content: stretch flex-end; align-items: stretch; } [fxLayoutAlign='start center'] { place-content: center flex-start; align-items: center; } [fxLayoutAlign='start start'] { place-content: flex-start; align-items: flex-start; } [fxLayoutAlign='space-between center'] { place-content: center space-between; align-items: center; } [fxLayoutAlign='start stretch'] { place-content: stretch flex-start; align-items: stretch; max-height: 100%; } [fxLayoutAlign='end'] { place-content: stretch flex-end; align-items: stretch; } [fxLayoutAlign='center stretch'] { place-content: stretch center; align-items: stretch; max-height: 100%; } [fxLayoutAlign='center center'] { place-content: center; align-items: center; } [fxLayoutAlign='center end'] { place-content: flex-end center; align-items: flex-end; } [fxLayoutAlign='stretch stretch'] { place-content: stretch flex-start; align-items: stretch !important; } // fxHide [fxHide] { display: none; } // `.` can't be part of attribute selector :( // fxHide.lt-lg @media screen and (max-width: 1279px) { [fxHidelt-lg] { display: none !important; } } // fxHide.gt-md @media screen and (min-width: 1280px) { [fxHidegt-md] { display: none !important; } } // fxShow.gt-sm @media screen and (min-width: 960px) { [fxShowgt-sm] { display: initial; } } // fxShow.gt-xs @media screen and (min-width: 600px) { [fxShowgt-xs] { display: initial; } } // fxFill [fxFill] { height: 100%; min-height: 100%; min-width: 100%; width: 100%; } ```
DuncanFaulkner commented 1 year ago

Thank you all for your feedback and support on this I will try and collate all the information and put something together.

runeabrahams1 commented 1 year ago

Thank you all for your feedback and support on this I will try and collate all the information and put something together.

@DuncanFaulkner Please consider accepting sponsorship if that helps in your decision.

As for features, nor sure if everyone agrees, but I was hoping this feature would eventually be implemented: #1185 Ofc only if the most common use-cases has an automated migration

Updating the library with new releases of Angular within 2-3 weeks, and just keeping the Directive features, would be a minimal requirement for us to keep using this library.

michaelfaith commented 1 year ago

As for features, nor sure if everyone agrees, but I was hoping this feature would eventually be implemented: #1185 Ofc only if the most common use-cases has an automated migration

Idk, I feel like this would end up creating just as much re-work for people as moving off of the library entirely. I mean, the performance improvements would certainly be attractive, but I wonder if some of that can be achieved with the new Directive Composition API they just rolled out in v15 (which could allow the public API to remain unchanged).

jpike88 commented 1 year ago

I attempted to migrate to pure css. It is a major, major PITA, and it reminded me why I use flex-layout in the first place! So I am much more happy to stick with a spinoff of flex-layout., and will happily contribute if needed.

Anthony-Wratpor commented 1 year ago

Hello i have done a different approach where I replaced the fxlayouts and fxLayoutAlign with CSS Classes and replaced all of the occurences in my code.

I have made a corresponding table to replicate this on other applications and it looks something like this :

<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">

Before | After -- | -- fxLayout="column" | class="flex-col" fxLayout="column" fxLayoutAlign="space-evenly stretch" | class="flex-col space-evenly stretch" fxLayout="column" fxLayoutAlign="space-evenly center" | class="flex-col space-evenly align-center" fxLayout="column" fxLayoutAlign="center space-evenly" | class="flex-col space-evenly justify-center" fxLayout="column" fxLayoutAlign="center stretch" | class="flex-col stretch justify-center" fxLayout="column" fxLayoutAlign="center center" | class="flex-col justify-center align-center" fxLayout="column" fxLayoutAlign="space-around center" | class="flex-col space-around align-center" fxLayout="column" fxLayoutAlign="space-between center" | class="flex-col space-between align-center" fxLayout="column" fxLayoutAlign="start stretch" | class="flex-col start stretch" fxLayout="column" fxLayoutAlign="start start" | class="flex-col start flex-start"   |   fxLayout="row" | class="flex-row" fxLayout="row" fxLayoutAlign="space-evenly center" | class="flex-row space-evenly align-center" fxLayout="row" fxLayoutAlign="space-evenly stretch" | class="flex-row space-evenly align-stretch" fxlayout="row" fxLayoutAlign="center center" | class="flex-row justify-center align-center" fxLayout="row" fxLayoutAlign="center stretch" | class="flex-row align-center align-stretch" fxLayout="row" fxLayoutAlign="start center" | class="flex-row start align-center" fxLayout="row" fxLayoutAlign="end center" | class="flex-row end align-center" fxLayout="row" fxLayoutAlign="space-between center" | class="flex-row space-between align-center" fxLayout="row wrap" fxLayoutAlign="space-evenly center" | class="flex-row flex-wrap space-evenly align-center" fxLayout="row wrap" fxLayoutAlign="start start" | class="flex-row flex-wrap start flex-start"

With theses css classes

```css // Flex row/col + grid. .flex-row { display: flex; flex-direction: row; } .flex-col { display: flex; flex-direction: column; } .grid { display: grid; } .grid { display: grid; } .flex-wrap { flex-wrap: wrap; } .flex-col-xs { @media screen and (max-width: 599px) { flex-direction: column; } } .flex-col-sm { @media screen and (min-width: 959px) { flex-direction: column; } } .flex-row-xs { @media screen and (max-width: 599px) { flex-direction: row; } } .flex-row-sm { @media screen and (min-width: 959px) { flex-direction: row; } } .gap-5 { gap: 5px; } .gap-10 { gap: 10px; } .gap-15 { gap: 15px; } .gap-14 { gap: 14px; } .gap-20 { gap: 20px; } .flex-auto { flex: 1 1 auto; } .space-between { justify-content: space-between; } .space-around { justify-content: space-around; } .space-evenly { justify-content: space-evenly; } .stretch { justify-content: stretch; } .align-stretch { align-content: stretch } .start { justify-content: start; } .content-start { align-content: flex-start; } .flex-start { align-items: flex-start; } .end { justify-content: end; } .flex-end { align-items: flex-end; } .justify-center { justify-content: center; } .align-center { align-items: center; } .align-baseline { align-items: baseline; } .flex { display: flex !important; } .flex-1 { flex: 1 } .flex-grow { flex-grow: 1; } .center { display: block; margin-left: auto; margin-right: auto; } ```

As you can see I also added gap classes but i added them manually after the big replace.

I don't think it's perfect in any way, feel free to post your own spin on it, but it kinda works for me so I might as well share it.

I think it's a huge mistake from the angular team to drop this when it's used that much, when there's no 1 to 1 replacement and knowing that it'll break many many applications..

jpike88 commented 1 year ago

The angular team has a whole trail of drama and short-sighted decisions behind it, we can all agree. But we need to pay out respects @CaerusKaru for making this possible. It's a stroke of genius this library, I have worked on a range of OSS bundlers and linters, this is like having teeth, you don't know how important they are until they're removed.

And at the same time, despite the pain and time wasted we may be feeling from this, we need to pay our respects to the efforts that have made Angular what it is, because without it, this library wouldn't exist. We stand on the shoulders of giants.

I'll happily tear into the decision to keep this library beta and finally kill it (which I still maintain as a silly silly thing to do), but we have to accept that it is their decision to make.

Let's make lemonade out of lemons, and entertain two clear possible pathways:

Anything else is a waste of our collective time.

zijianhuang commented 1 year ago

I have a complex business app with around 1000 angular flex layout directives. It had taken me 3 days including the time to learn CSS flex and examples by other commentators in this thread to migrate to pure CSS even though many of the directives had been replaced through search & replace. I had thought of writing a tool, however I was thinking, it may take me 20 hours to write this tool, while the angular flex layout team may just need 10 hours and produce much better results for different scenarios.

If I develop such tool, I would probably take such approach.

  1. Have some CSS classes that could replace some directives easily.
  2. A tool (probably coded in JavaScript/TS) scans nominated HTML of the SPA, replace some directives with CSS classes, or append to existing class attribute of an element.
  3. Along the scanning, generate some new CSS classes, for example for those "fxFlex" directives with different length, "grow" and "shrink", like class="some-existing-class flex-27 fx-grow-2 fx-shrink-1".
  4. For scenarios that a tool can not handle, leave some annotations near respective elements so programmers may manually modify.

I believe angular flex layout is capable of better designs. They had made such beautiful baby angular flex layout, hopefully they are going to give it a beautiful funeral.

yuveliremil commented 1 year ago

Angular Team ! @andrewseguin, @mgechev, @filipesilva, @gkalpak, @jessicajaniuk, @crisbeto, @devversion, @simonaco, @waterplea, @alisaduncan and etc.

What do you think about this? Why should people have bad thoughts about Angular because of this deprecation?

@CaerusKaru, you say below message last mounth:
We will publish a v15 version. This will unpin the dependencies so, barring any structural breaking changes, this will not happen in the near future. We hope to get this version out this coming week.

We still wait for the v15.

Franweb79 commented 1 year ago

Angular Team ! @andrewseguin, @mgechev, @filipesilva, @gkalpak, @jessicajaniuk, @crisbeto, @devversion, @simonaco, @waterplea, @alisaduncan and etc.

What do you think about this? Why should people have bad thoughts about Angular because of this deprecation?

@CaerusKaru, you say below message last mounth: We will publish a v15 version. This will unpin the dependencies so, barring any structural breaking changes, this will not happen in the near future. We hope to get this version out this coming week.

We still wait for the v15.

Seems they answered something on this closed issue (closed because it was "too heated", as if what they are doing was not worthy of it), also showing us how commited they are because they state there they give us at least a year of TLS support while we waste the time fixing and paying the consequences of their decission ( aka "we plan for migrations").

https://github.com/angular/flex-layout/issues/1430#issuecomment-1363578061

Maybe they will delete this post or mark it as off-topic, as they do with any uncomfortable truth, but just wanted to point to a response they gave today.

If it is frustrating for them, as they say, which don´t have to face the consequences of what they have done (regarding applications, because for the image of Angular, Google and its team, those consequences are clear), imagine for us.

But well, who cares, I am learning React. Merry Christmas.

philmtd commented 1 year ago

Just in case anyone is interested and reads this: I've created a library inspired by this one a few years ago and just updated it a little. It does not replicate everything Flex Layout is capable of, but I think the most popular features are available and it might be a good alternative for at least smaller projects: https://github.com/philmtd/css-fx-layout

erenerdilli commented 1 year ago

@CaerusKaru is there an estimated date for release of v15, since the holidays are over? We are quite intertwined with this library in our applications, and the migration brings a lot of challenges with tight deadlines.

alanpurple commented 1 year ago

hide-related directives are most critical since angular-material components cannot be hidden with class definition "display:none"

display:none should be applied for every components with style or [ngStyle]

alanpurple commented 1 year ago

@arambazamba

define class with "display:none" does not work with angular-material components

emonney commented 1 year ago

I created this sass css file to handle the common flex use cases flex-layout.scss Give it a star for me will ya!

How to Use

e.g. 1 - In your html files:

  <div class="flex justify-around align-center">
      ...
  </div>

e.g. 2 - In your sass css files

  // style.scss
  @use 'flex-layout'; //import it

  .some-div {
    margin-right: 16px;

    @include flex-layout.breakpoint('lt-sm') { //use the breakpoint mixin
        margin-right: 0;
    }
  }

Note: Modify this file with your own use cases/additions to handle unique flexbox requirements for your project. Treat it as just another sass file in your project that you use for general flex styles

Regards, www.ebenmonney.com

iKrishnaSahu commented 1 year ago

@CaerusKaru

Just started working on migration.

We had created a custom directive on top of BaseDirective2 which toggles the visibility of a element based on the viewport size. We are heavily using this directive in our component library and our internal clients are also using it. I don't have the exact count of this custom directive's usage.

My question is - What is the best alternative of BaseDirective2?

I am currently exploring BreakpointObserver. This may help me to replace BaseDirective2

Update - I successfully replaced BaseDirective2 with BreakpointObserver

pashvin commented 1 year ago

Here is one full replacement for the angular flex library - https://www.npmjs.com/package/ngx-flexible-layout. Has the same directive names so no change in any HTML file. Just replace package imports and it should work.

celalettin-turgut commented 1 year ago

Hi guys, it seems all the concern is about the migration after the deprecation. But for the new projects it is not easy to decide what to use as CSS library along with Angular + Angular Components. The best solution seems to be using pure css but it is much more work. Do you have any idea, recommedations etc.?

michaelfaith commented 1 year ago

Hi guys, it seems all the concern is about the migration after the deprecation. But for the new projects it is not easy to decide what to use as CSS library along with Angular + Angular Components. The best solution seems to be using pure css but it is much more work. Do you have any idea, recommedations etc.?

Tailwind CSS has some very similar capabilities and has been supported by Angular for a couple versions now. That would be my personal recommendation for a brand new app.

celalettin-turgut commented 1 year ago

Thank you @michaelfaith for the recommendation. What are the advantages and disadvantages using Tailwind css? What should we consider before using it? Don't you think it is an overkill to use angular components as ui library and Tailwind only for layout purposes?

thoxx commented 1 year ago

If someone uses Flex-Layout primarily for layouting with columns and rows, it may be possible to migrate with CSS Grid and an Angular directive with moderate effort:

Angular Flex-Layout example:

<div fxLayout="row">
    <div fxFlex="40%">[...]</div>
    <div fxFlex="15%">[...]</div>
    <div fxFlex="45%">[...]</div>
</div>

Angular Directive:

import { Directive, HostBinding, Input } from '@angular/core';

@Directive({
  selector: '[grid]'
})
export class GridDirective {

  @HostBinding('style.display') display = 'grid';

  @Input() columns: string | null = null;
  @Input() rows: string | null = null;

  @HostBinding('style.grid-template-columns')
  get gridTemplateColumns(): string | null {
    return this.columns ? this.columns : null;
  }

  @HostBinding('style.grid-template-rows')
  get gridTemplateRows(): string | null {
    return this.rows ? this.rows : null;
  }
}

Replacement of example with Grid:

<div grid columns="40% 15% 45%">
    <div>[...]</div>
    <div>[...]</div>
    <div>[...]</div>
</div>

The directive can then be easily extended for other cases.

DuncanFaulkner commented 1 year ago

I've now setup a clone of the Angular Flex-Layout library- this is still very much at the early stages but I've got the GitHub repo set up along with the Circle CI and a package on NPM.

The new home is now at

@ngbracket/ngx-layout.

or copy this link https://github.com/ngbracket/ngx-layout

I've pushed up a pre-release version 15.0.1-beta.42, this version is just an initial release just to make sure things are working, the only thing that got changed was the name which has been changed from @angular to @ngbracket, some of the readme's, and documentation still need to be updated to reflect this change which I'm hoping to complete shortly.

Please do check it out I welcome all feedback

Sergiobop commented 1 year ago

Hi @DuncanFaulkner , thank you. Can you edit and add in your comments the link to the project so it's easier to find? (https://github.com/ngbracket/ngx-layout)

DuncanFaulkner commented 1 year ago

@Sergiobop updated, sorry thought I had added it as a link

DuncanFaulkner commented 1 year ago

I've now setup a clone of the Angular Flex-Layout library- this is still very much at the early stages but I've got the GitHub repo set up along with the Circle CI and a package on NPM.

The new home is now at

@ngbracket/ngx-layout.

I've pushed up a pre-release version 15.0.1-beta.42, this version is just an initial release just to make sure things are working, the only thing that got changed was the name which has been changed from https://github.com/angular/flex-layout to https://github.com/ngbracket/ngx-layout, some of the readme's, and documentation still need to be updated to reflect this change which I'm hoping to complete shortly.

Please do check it out I welcome all feedback

afnecors commented 1 year ago

With primer/css you can import flexbox utilities (and dependencies) in your style.scss:

@import "@primer/css/support/mixins/layout.scss";
@import "@primer/css/support/variables/layout.scss";
@import "@primer/css/utilities/visibility-display.scss";
@import "@primer/css/utilities/flexbox.scss";

So, you can migrate from

<div fxLayout="row" fxLayoutAlign="space-between center">
  <div>1</div>
  <div>2</div>
</div>

To

<div class="d-flex flex-row flex-justify-between flex-content-center">
  <div>1</div>
  <div>2</div>
</div>

For more info, see https://primer.style/css/utilities/flexbox

wassim-k commented 1 year ago

Here's a basic javascript code for migrating flex layout directives to tailwindcss classes.

It supports fxLayout, fxLayoutAlign, fxFlex, fxGap & fxFill directives, which covered 95% of our project, the rest I updated manually.

mkdir flex-layout-to-tailwind
cd flex-layout-to-tailwind
npm i cheerio
code index.js # this opens a vscode window with an empty index.js file
# Paste the code below.
# Replace [Enter path to angular application here] with the correct path.
node index.js
const fs = require('fs');
const path = require('path');
const { load } = require('cheerio');

const mainAxisMap = {
    'start': 'justify-start',
    'center': 'justify-center',
    'end': 'justify-end',
    'space-around': 'justify-around',
    'space-between': 'justify-between',
    'space-evenly': 'justify-evenly',
};

const crossAxisMap = {
    'start': 'items-start',
    'center': 'items-center',
    'end': 'items-end',
    'baseline': 'items-baseline',
    'stretch': 'items-stretch',
};

const fxAttributes = [
    'fxFill',
    'fxLayout',
    'fxLayoutAlign',
    'fxGap',
    'fxFlex'
];

function convertFlexLayoutToTailwind(filePath) {
    const html = fs.readFileSync(filePath, 'utf-8');
    return extractHtmlTags(html)
        .reduce((html, tag) => html.replace(tag, convertTag(tag)), html);
};

function convertTag(tag) {
    if (!fxAttributes.some(a => tag.includes(a))) return tag;

    const $ = load(tag, { xmlMode: true, decodeEntities: false });

    $('[fxLayout], [fxLayoutGap], [fxLayoutAlign]').each((_, element) => {
        const $element = $(element);

        const fxLayout = $element.attr('fxLayout');
        const fxLayoutGap = $element.attr('fxLayoutGap');
        const fxLayoutAlign = $element.attr('fxLayoutAlign');

        if (fxLayout) {
            convertFxLayoutToTailwind($element, fxLayout);
        }

        if (fxLayoutGap) {
            convertFxLayoutGapToTailwind($element, fxLayout, fxLayoutGap);
        }

        if (fxLayoutAlign) {
            const [mainAxis, crossAxis] = fxLayoutAlign.split(' ');

            if (mainAxis !== 'start' && crossAxis !== 'start') {
                $element
                    .addClass(`${mainAxisMap[mainAxis] || ''} ${crossAxisMap[crossAxis] || ''}`)
                    .removeAttr('fxLayoutAlign');
            } else if (mainAxis !== 'start') {
                $element
                    .addClass(`${mainAxisMap[mainAxis] || ''}`)
                    .removeAttr('fxLayoutAlign');
            } else {
                $element
                    .addClass(crossAxisMap[crossAxis] || '')
                    .removeAttr('fxLayoutAlign');
            }
        }
    });

    $('[fxFlex]').each((_, elem) => {
        let fxFlex = $(elem).attr('fxFlex');

        if (!fxFlex) {
            $(elem)
                .addClass(`flex-1`)
                .removeAttr('fxFlex');
            return;
        }

        let widthClass = '';
        switch (+fxFlex) {
            case 33: widthClass = '1/3'; break;
            case 66: widthClass = '2/3'; break;
            case 100: widthClass = 'full'; break;
            default: widthClass = percentageToFraction(+fxFlex); break;
        }

        $(elem)
            .addClass(`basis-${widthClass}`)
            .removeAttr('fxFlex');
    });

    $('[fxFill]').each((_, elem) => {
        $(elem)
            .addClass(`h-full w-full min-h-full min-w-full`)
            .removeAttr('fxFill');
    });

    let newTag = $.html();
    newTag = newTag.replace(/(\W\w+)=""/gm, '$1');

    if (newTag.endsWith('/>') && tag.endsWith('/>')) {
        return newTag;
    } else {
        return newTag.slice(0, -2) + '>';
    }
}

function convertFxLayoutToTailwind($element, fxLayout) {
    let [layout, other] = (fxLayout || 'column').split(' ');

    let className = '';
    switch(layout) {
        case 'row': className = 'flex-row'; break;
        case 'column': className = 'flex-col'; break;
        case 'row-reverse': className = 'flex-row-reverse'; break;
        case 'column-reverse': className = 'flex-col-reverse'; break;
        default: return;
    }

    $element.addClass(`flex ${className}`);

    if (other === 'wrap') {
        $element.addClass('flex-wrap');
    }

    if (other === 'inline') {
        $element.removeClass('flex');
        $element.addClass('inline-flex');
    }

    $element.removeAttr('fxLayout');
};

function convertFxLayoutGapToTailwind($element, fxLayout, fxLayoutGap) {
    let [layout] = (fxLayout || 'column').split(' ');

    if (fxLayoutGap === undefined) return;

    const spacing = Math.ceil(parseFloat(fxLayoutGap) * 4); // convert from em

    if (layout === 'row') {
        $element.addClass(`space-x-${spacing}`);
    } else {
        $element.addClass(`space-y-${spacing}`);
    }

    $element.removeAttr('fxLayoutGap');
};

function gcd(a, b) {
    if (!b) {
        return a;
    }
    return gcd(b, a % b);
}

function percentageToFraction(percentage) {
    const denominator = 100;
    const numerator = percentage;
    const gcdValue = gcd(numerator, denominator);
    const simplifiedNumerator = numerator / gcdValue;
    const simplifiedDenominator = denominator / gcdValue;
    return `${simplifiedNumerator}/${simplifiedDenominator}`;
}

function extractHtmlTags(html) {
    let openingTags = [];
    let tag = "";
    let inTag = false;
    let quote = null;

    for (const ch of [...html]) {
        if (!inTag && ch === "<") {
            inTag = true;
            tag += ch;
        } else if (inTag) {
            tag += ch;

            if (quote === null && (ch === "\"" || ch === "'")) {
                quote = ch;
            } else if (quote !== null && ch === quote) {
                quote = null;
            } else if (quote === null && ch === ">") {
                openingTags.push(tag);
                tag = "";
                inTag = false;
            }
        }
    }

    return openingTags;
}

function convertFile(filePath) {
    const convertedData = convertFlexLayoutToTailwind(filePath);
    fs.writeFileSync(filePath, convertedData, 'utf-8');
    console.log(`File converted successfully: ${filePath}`);
};

function processFiles(
    folderPath,
    processFile,
    processFolder,
    level = 0
) {
    if (fs.existsSync(folderPath)) {
        fs.readdirSync(folderPath).forEach(file => {
            const currentPath = path.join(folderPath, file);
            if (fs.lstatSync(currentPath).isDirectory()) {

                if (currentPath.endsWith('node_modules') || currentPath.endsWith('dist')) {
                    return;
                }

                if (processFiles(currentPath, processFile, processFolder, level + 1)) {
                    processFolder?.(currentPath);
                }
            } else {
                if (currentPath.endsWith('.html')) {
                    processFile(currentPath, level);
                }
            }
        });
        return true;
    } else {
        return false;
    }
}

processFiles('[Enter path to angular application here]', convertFile);
chrispharmac commented 1 year ago

@wassim-k Thanks much for this! I hacked on it a bit more, and as it did for you, it took care of around 90% of the job. I couldn't figure out how to do the media queries, perhaps someone can. But our project only used them in a few places, so it was quite easy to change those by hand.

index.js.txt

jeffschulzusc commented 1 year ago

For our current monorepo, replacing FlexLayout with ex. TailwindCSS would not be practical. We will try and support https://github.com/ngbracket/ngx-layout

Statistics (instances/files):

fxFlex ... 3395 / 523
fxLayout ... 6214 / 649
fxLayoutGap ... 614 / 287
fxLayoutAlign ... 2570 / 629
fxFlexAlign ... 110 / 88
fxFill ... 138 / 55
fxHide,fxShow ... 199 / 52
.xs,...,gt-lg ... 1315 / 535
ngClass.* ... 481 / 207
ngStyle.* ... 20 / 14

@Component 808 / 806
@Injectable 173 / 155

Going forward with new components we will just use css flex-grid ... but so that we can keep the styling/layout in the html template, here are some rudimentary mixins that i cooked up (with help from other's posts):

@mixin m_vxFlex($dir, $axa, $axb) {
  display: flex;
  flex-direction: $dir;
  flex-wrap: nowrap;
  align-content: normal;
  justify-content: $axa;
  align-items: $axb;
}

[vxFlex~='csc'] { // fxLayout="column" fxLayoutAlign="start center"
  @include m_vxFlex(row, flex-start, flex-start);
}
... each permutation

@mixin m_vxFlexXS($dir, $axa, $axb) {
  @media (max-width: $XS) {
    display: flex !important;
    flex-direction: $dir !important;
    flex-wrap: nowrap !important;
    align-content: normal !important;
    justify-content: $axa !important;
    align-items: $axb !important;
  }
}

[vxFlexXS~='rss'] { // fxLayout="row" fxLayoutAlign="start start"
  @include m_vxFlexXS(row, flex-start, flex-start);
}
... each permutation 

[vxHideXS] { // fxHide / fxShow
  @media (max-width: $XS) {
    display: none !important;
  }
}

example:
<div vxFlex="rsc" vxHideXS> ...

And some basic directives:

@Directive({
  selector: '[vxHGap]', // fxFlex="n"
  standalone: true,
})
export class VXHGapDirective implements OnInit {
  @Input('vxHGap') gap: string | undefined;

  constructor(private readonly elementref: ElementRef<HTMLElement>) {}

  ngOnInit(): void {
    this.elementref.nativeElement.style.width = this.gap ?? '';
  }
}

@Directive({
  selector: '[vxGrow]', // fxFlex="auto"
  standalone: true,
})
export class VXGrowDirective implements OnInit {
  @Input('vxGrow') grow: string | undefined;

  constructor(private readonly elementref: ElementRef<HTMLElement>) {}

  ngOnInit(): void {
    this.elementref.nativeElement.style.flexGrow = this.grow ?? '';
  }
}

Note that this is fairly simple (on purpose) and does not consider its parent nor children styles.

Cylop commented 1 year ago

Hi everyone! 👋

As we all know, this issue is a concern for many developers who rely on it. In response to this, I've started an open-source project with the goal of helping everyone migrate from the deprecated attributes to their modern alternatives.

🔗 Here's the link to the project: https://github.com/NIPE-Solutions/flex-layout-migrator

Currently, the basic structure is in place, but we're still working on implementing the actual mechanisms to replace the old attributes. Any help, feedback, or suggestions are more than welcome, as this is a community-driven project and we want to make it as useful as possible for everyone.

If you're interested in contributing or have any questions, please don't hesitate to join the project or reach out to me. Together, we can make this migration process smoother and more efficient for everyone involved. Thank you! 😊

maxfriedmann commented 1 year ago

@wassim-k Many thanks for your code, I added another finding from myself (fxFlex=* appeared in our code ???), added another fix from @chrispharmac (spacing px should be divided by 4) and published it as https://www.npmjs.com/package/migrate-angular-flex-to-tailwind I hope thats fine with you.

We actually managed to get 100% migrated with your code so I thought it might worth it to publish it.

wassim-k commented 1 year ago

@maxfriedmann glad you found it useful and more than happy for it to be published, the more people it reaches the better.

AngularTx commented 1 year ago

Hi All,

We have also been using this package for our project . But we would probably be freezing the version to Angular 15 and for this package and no plans to upgrade going forward .

So the question is . For this scenario, is it still OK to continue with this package and I don't plan to migrate my application to any version more than 15. This project is kind of internal application and we are fine with how it works till Angular 15 with the last version of flex layout also.

chrispharmac commented 1 year ago

I was unable to upgrade to 15 until I migrated, even with --legacy-peer-deps. But you may have better luck. Mine was complicated by needing to upgrade material components also.

blogcraft commented 1 year ago

@wassim-k Many thanks for your code, I added another finding from myself (fxFlex=* appeared in our code ???), added another fix from @chrispharmac (spacing px should be divided by 4) and published it as https://www.npmjs.com/package/migrate-angular-flex-to-tailwind I hope thats fine with you.

We actually managed to get 100% migrated with your code so I thought it might worth it to publish it.

There where alot of attributes it does not convert, some examples: fxFlexFill fxFlex.lt-md="0 0 calc(50% - 10px)" fxLayout.sm="row wrap"

Somehow it translated this: fxLayout.sm="row wrap" fxLayout.xs="column" fxFlex="50%" Into this: fxLayout.sm="row wrap" fxLayout.xs="column" class="basis-NaN/1"

and this: class="flex flex-row flex-wrap space-x-3 justify-between items-center basis-NaN/1" into this: fxLayout="row wrap" fxLayoutGap="10px" fxFlex="50%" fxLayoutAlign="space-between center"

shripadgodse commented 1 year ago

Tailwind

Were you able to migrate from flex to any other framework ? We are also having around 6/10 projects which are using FlexLayout. Let us know if you guys have any solution.

michaelfaith commented 1 year ago

Tailwind

Were you able to migrate from flex to any other framework ? We are also having around 6/10 projects which are using FlexLayout. Let us know if you guys have any solution.

For one of our libraries, which made minimal use of Flex Layout, we just shifted to pure css to avoid another dependency. But for our apps, which made extensive use, we moved to TailwindCSS

DuncanFaulkner commented 1 year ago

@shripadgodse just to let you know I have forked this project and updated it to use version 16 of Angular, I had to rename the project but that is a minimal change in your project. I understand that (in the mid to long term) you need to find an alternative for this library, but I plan on supporting this library for the foreseeable future to help those that have a number of projects that require migration to something more modern but don't have the time, resources or the budget to do so.

The project can be found on NPM ngx-layout and the repo can be found ngx-layout.

I hope this helps by giving you time to port your projects.

If you would like to sponsor the project you can find details on our sponsor page.

chrispharmac commented 1 year ago

@@.***> We only had one app which wasn’t too complicated. But it sounded like TailwindCSS was the way to go. Being a new angular developer, I really appreciated the flex to tailwind script that was contributed here in the discussion, it got me most of the way there. I can see that Tailwind is strongly supported, and it has a great set of documentation pages.

Chris Tillman | Software Developer Te Pātaka Whaioranga | Pharmac | PO Box 10-254 | Level 9, 40 Mercer Street, Wellington P: 0800 660 050 | M: +64 27 505 9020 | www.pharmac.govt.nzhttp://www.pharmac.govt.nz/

From: shripadgodse @.> Sent: Wednesday, June 14, 2023 12:06 AM To: angular/flex-layout @.> Cc: Chris Tillman @.>; Mention @.> Subject: Re: [angular/flex-layout] Angular Layout Migration Guides (Issue #1426)

Tailwind

Were you able to migrate from flex to any other framework ? We are also having around 6/10 projects which are using FlexLayout. Let us know if you guys have any solution.

— Reply to this email directly, view it on GitHubhttps://github.com/angular/flex-layout/issues/1426#issuecomment-1589173094, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AZU2LJW65IXWMNIUOH2ZZT3XLBJUJANCNFSM6AAAAAARHPHNBI. You are receiving this because you were mentioned.Message ID: @.**@.>>

This e-mail message and any accompanying attachments may contain confidential information. If you are not the intended recipient, please do not read, use, disseminate, distribute or copy this message or attachments. If you have received this message in error, please notify the sender immediately and delete this message.

guoapeng commented 1 year ago

no one mentened about a attribute like the following [fxLayoutAlign]="(settings.menuType=='default') ? 'start center' : 'center center'", any idea on converting the above attribute into Tailwind?

lasimard commented 11 months ago

oes add style properties to the parent element as well and not just the elment itself.

<div fxFlex><div class="flex-1">.

@anisabboud I had a similar idea. But adding fxFlex does add style properties to the parent element as well and not just the elment itself.

<div>
    <div>
        test 1
        <div fxFlex>test 2</div>
    </div>
</div>
<div>
    <div>
        test 1
        <div class="flex-1">test 2</div>
    </div>
</div>

is not the same thing. Using the new :has() selector should work however

Has anyone found a solution for this issue with fxFlex. Our use looks like

<div fxFlex="1 1 auto">Content</div>

.c3-flex-1-1-auto { flex: 1 1 auto; }

It does not translate 1:1 wjat fxFlex does

Cylop commented 11 months ago

Hello Everyone,

Just a brief reminder for those looking for migration solutions: I've created an open-source tool to help migrate from ngFlex to TailwindCSS. It's not perfect, but it might make the process a bit easier for some!

Flex Layout Migrator

Please don't hesitate to check it out, and any feedback is welcome!

Best, Nicholas

synopss commented 11 months ago

Hello,

I've developed a CLI designed to simplify the migration process from flex-layout to tailwindcss. The primary goal of this CLI is to automate the entire process, minimizing the need for manual intervention. Ideally, you should not have to make any additional changes once the CLI completes its tasks.

Here's what the CLI does for you:

Since it's a CLI tool, you can install it globally and smoothly execute it across multiple projects.

My objective with this CLI is to offer a comprehensive 1-to-1 migration solution. Some existing solutions in the discussion thread do not adequately handle some specific cases, which I have taken care to address.

Please note that this project is still a work in progress. While you can already use the tool, it's currently in a release candidate state. At present, all flex directives are supported, and my next target is to cover grid directives.

no one mentened about a attribute like the following [fxLayoutAlign]="(settings.menuType=='default') ? 'start center' : 'center center'", any idea on converting the above attribute into Tailwind?

Unfortunately, this part may require manual adjustment for now. I'll continue working on finding a solution, but I cannot guarantee a quick resolution to this particular issue. Same story for custom breakpoints.

Feel free to open issues if you notice any problem, any contribution is also welcomed.

Link: https://github.com/synopss/flex-layout-to-tailwind

reuse-ay commented 7 months ago

We replaced ~2000 simple usages of flex-layout with following CSS. Kinda hacky, but seems to work.

CSS

// fxFlex
[fxFlex] {
    flex: 1 1 0%;
    box-sizing: border-box;
}

*:has(> [fxFlex]) {
    flex-direction: row;
    box-sizing: border-box;
    display: flex;
}

[fxFlex='5'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 5%;
}

[fxFlex='20'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 20%;
}

[fxFlex='25'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 25%;
}

[fxFlex='30'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 30%;
}

[fxFlex='33'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 33%;
}

[fxFlex='50'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 50%;
}

[fxFlex='66'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 66%;
}

[fxFlex='67'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 67%;
}

[fxFlex='70'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 70%;
}

[fxFlex='80'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 80%;
}

[fxFlex='100'] {
    flex: 1 1 100%;
    box-sizing: border-box;
    max-width: 100%;
}

[fxFlex='1 0 auto'] {
    flex: 1 0 auto;
    box-sizing: border-box;
}

[fxFlex='0 1 auto'] {
    flex: 0 1 auto;
    box-sizing: border-box;
}

// fxLayout
[fxLayout] {
    box-sizing: border-box;
    display: flex !important;
}

[fxLayout='row wrap'] {
    flex-flow: row wrap;
    flex: 1 1 1e-9px;
}

[fxLayout='row'] {
    flex-direction: row;
}

[fxLayout='column'] {
    flex-direction: column !important;

    &[fxLayoutGap='8px'] > *:not(:last-child) {
        margin-right: unset;
        margin-bottom: 8px;
    }
    &[fxLayoutGap='12px'] > *:not(:last-child) {
        margin-right: unset;
        margin-bottom: 12px;
    }
    &[fxLayoutGap='16px'] > *:not(:last-child) {
        margin-right: unset;
        margin-bottom: 16px;
    }
    &[fxLayoutGap='24px'] > *:not(:last-child) {
        margin-right: unset;
        margin-bottom: 24px;
    }
    &[fxLayoutGap='32px'] > *:not(:last-child) {
        margin-right: unset;
        margin-bottom: 32px;
    }

    & > [fxFlex='5'] {
        max-height: 5%;
        max-width: unset;
    }
    & > [fxFlex='20'] {
        max-height: 20%;
        max-width: unset;
    }
    & > [fxFlex='25'] {
        max-height: 25%;
        max-width: unset;
    }
    & > [fxFlex='30'] {
        max-height: 30%;
        max-width: unset;
    }
    & > [fxFlex='33'] {
        max-height: 33%;
        max-width: unset;
    }
    & > [fxFlex='50'] {
        max-height: 50%;
        max-width: unset;
    }
    & > [fxFlex='66'] {
        max-height: 66%;
        max-width: unset;
    }
    & > [fxFlex='67'] {
        max-height: 67%;
        max-width: unset;
    }
    & > [fxFlex='70'] {
        max-height: 70%;
        max-width: unset;
    }
    & > [fxFlex='80'] {
        max-height: 80%;
        max-width: unset;
    }
    & > [fxFlex='100'] {
        max-height: 100%;
        max-width: unset;
    }
}

// fxLayoutGap
[fxLayoutGap='8px'] > *:not(:last-child) {
    margin-right: 8px;
}
[fxLayoutGap='12px'] > *:not(:last-child) {
    margin-right: 12px;
}
[fxLayoutGap='16px'] > *:not(:last-child) {
    margin-right: 16px;
}
[fxLayoutGap='24px'] > *:not(:last-child) {
    margin-right: 24px;
}
[fxLayoutGap='32px'] > *:not(:last-child) {
    margin-right: 32px;
}

// fxLayoutAlign
[fxLayoutAlign] {
    display: flex;
    box-sizing: border-box;
    flex-direction: row;
}
[fxLayoutAlign='center center'] {
    place-content: center;
    align-items: center;
}
[fxLayoutAlign='end center'] {
    place-content: center flex-end;
    align-items: center;
}
[fxLayoutAlign='end top'] {
    place-content: stretch flex-end;
    align-items: stretch;
}
[fxLayoutAlign='start center'] {
    place-content: center flex-start;
    align-items: center;
}
[fxLayoutAlign='start start'] {
    place-content: flex-start;
    align-items: flex-start;
}
[fxLayoutAlign='space-between center'] {
    place-content: center space-between;
    align-items: center;
}
[fxLayoutAlign='start stretch'] {
    place-content: stretch flex-start;
    align-items: stretch;
    max-height: 100%;
}
[fxLayoutAlign='end'] {
    place-content: stretch flex-end;
    align-items: stretch;
}
[fxLayoutAlign='center stretch'] {
    place-content: stretch center;
    align-items: stretch;
    max-height: 100%;
}
[fxLayoutAlign='center center'] {
    place-content: center;
    align-items: center;
}
[fxLayoutAlign='center end'] {
    place-content: flex-end center;
    align-items: flex-end;
}
[fxLayoutAlign='stretch stretch'] {
    place-content: stretch flex-start;
    align-items: stretch !important;
}

// fxHide
[fxHide] {
    display: none;
}

// `.` can't be part of attribute selector :(
// fxHide.lt-lg
@media screen and (max-width: 1279px) {
    [fxHidelt-lg] {
        display: none !important;
    }
}

// fxHide.gt-md
@media screen and (min-width: 1280px) {
    [fxHidegt-md] {
        display: none !important;
    }
}

// fxShow.gt-sm
@media screen and (min-width: 960px) {
    [fxShowgt-sm] {
        display: initial;
    }
}

// fxShow.gt-xs
@media screen and (min-width: 600px) {
    [fxShowgt-xs] {
        display: initial;
    }
}

// fxFill
[fxFill] {
    height: 100%;
    min-height: 100%;
    min-width: 100%;
    width: 100%;
}

This may work about period. @media screen and (max-width:600px) { [fxHide\.xs]:not([fxHide\.xs="false"]) { display: none!important; } }

ayan-bloomscorp commented 7 months ago

Hello,

I've developed a CLI designed to simplify the migration process from flex-layout to tailwindcss. The primary goal of this CLI is to automate the entire process, minimizing the need for manual intervention. Ideally, you should not have to make any additional changes once the CLI completes its tasks.

Here's what the CLI does for you:

  • It automatically installs tailwindcss.
  • Removes the old flex-layout dependency.
  • Converts all directives into tailwindcss classes.

Since it's a CLI tool, you can install it globally and smoothly execute it across multiple projects.

My objective with this CLI is to offer a comprehensive 1-to-1 migration solution. Some existing solutions in the discussion thread do not adequately handle some specific cases, which I have taken care to address.

Please note that this project is still a work in progress. While you can already use the tool, it's currently in a release candidate state. At present, all flex directives are supported, and my next target is to cover grid directives.

no one mentened about a attribute like the following [fxLayoutAlign]="(settings.menuType=='default') ? 'start center' : 'center center'", any idea on converting the above attribute into Tailwind?

Unfortunately, this part may require manual adjustment for now. I'll continue working on finding a solution, but I cannot guarantee a quick resolution to this particular issue. Same story for custom breakpoints.

Feel free to open issues if you notice any problem, any contribution is also welcomed.

Link: https://github.com/synopss/flex-layout-to-tailwind

@synopss I have raised couple of issues/PR for your migration. Among the ones I know it is working best out of them, but it lacks in some specific use cases. I will be happy to contribute to it. Let me know if you have enough time to maintain the codebase.

synopss commented 7 months ago

@ayan-bloomscorp thanks for your inputs, I'll check asap.

melroy89 commented 4 months ago

Using flex layout with hot cache (fast rebuild) in my Angular app in around 200-300ms:

Initial chunk files | Names   |  Raw size
main.js             | main    | 442.17 kB | 
runtime.js          | runtime |   6.46 kB | 

3 unchanged chunks

Build at: 2024-04-13T09:53:44.197Z - Hash: a8c99904de752972 - Time: 237ms

✔ Compiled successfully.

When I moved to Tailwind also with hot cache a rebuild takes 6500ms+:

Build at: 2024-04-13T09:52:23.268Z - Hash: 9752dba1b11aedf0 - Time: 6764ms

✔ Compiled successfully.

Initial chunk files | Names   |  Raw size
main.js             | main    | 441.88 kB | 
runtime.js          | runtime |   6.46 kB | 

3 unchanged chunks

Using tailwindcss/base, tailwindcss/components and tailwindcss/utilities. This is 30 times slower than angular flex layout.

Uh no thanks. This make rapid development a no go.

I go with the community fork: https://github.com/alessiobianchini/ng-flex-layout