0

I have the following code CODEPEN

HTML

body
    #__nuxt
        #__layout
            //-         CSS Grid
            .news-item(:key="item.feed_item_id" :ref="item.feed_item_id" role="listitem" tabindex="0")
                .news-item-pubdate 1m 
                .news-item-title(:style="titleStyle") {{item.title}}
                    span.news-item-link  example.com
                .news-item-likes
                    span.icon.is-small.news-item-vote-icon
                        i.fa.fa-xs.fa-thumbs-up
                    span.news-item-vote-count {{item.likes}}
                .news-item-dislikes
                    span.icon.is-small.news-item-vote-icon
                        i.fa.fa-xs.fa-thumbs-down
                    span.news-item-vote-count {{item.dislikes}}
                .news-item-bullish
                    span.icon.is-small.news-item-vote-icon
                        i.fa.fa-xs.fa-arrow-up
                    span.news-item-vote-count {{item.bullish}}
                .news-item-bearish
                    span.icon.is-small.news-item-vote-icon
                        i.fa.fa-xs.fa-arrow-down
                    span.news-item-vote-count {{item.bearish}}
                .news-item-comments
                    span.icon.is-small.news-item-vote-icon
                        i.fa.fa-xs.fa-comment-alt
                    span.news-item-vote-count 0
                .news-item-tags.has-text-right(:style="tagStyle")
                    .news-item-tag(v-for='(tag, i) in item.tags' :key='item.tag' @click.stop="updateTag(tag)")
                        a.button.is-paddingless.is-small.is-uppercase.is-text
                            |{{tag}}
                            span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
                            span(v-else)    
            //-             Flexbox
            .news-item2(:key="item.feed_item_id" :ref="item.feed_item_id" role="listitem" tabindex="0")
                .news-item-pubdate-wrapper
                    .news-item-pubdate2 1m  
                .news-item-content-wrapper
                    .news-item-title-wrapper
                        .news-item-title2(:style="titleStyle") {{item.title}}
                            span.news-item-link2  example.com
                    .news-item-votes-wrapper
                        .news-item-likes2
                            span.icon.is-small.news-item-vote-icon2
                                i.fa.fa-xs.fa-thumbs-up
                            span.news-item-vote-count2 {{item.likes}}
                        .news-item-dislikes2
                            span.icon.is-small.news-item-vote-icon2
                                i.fa.fa-xs.fa-thumbs-down
                            span.news-item-vote-count2 {{item.dislikes}}
                        .news-item-bullish2
                            span.icon.is-small.news-item-vote-icon2
                                i.fa.fa-xs.fa-arrow-up
                            span.news-item-vote-count2 {{item.bullish}}
                        .news-item-bearish2
                            span.icon.is-small.news-item-vote-icon2
                                i.fa.fa-xs.fa-arrow-down
                            span.news-item-vote-count2 {{item.bearish}}
                        .news-item-comments2
                            span.icon.is-small.news-item-vote-icon2
                                i.fa.fa-xs.fa-comment-alt
                            span.news-item-vote-count2 0
                .news-item-tags-wrapper
                    .news-item-tags2.has-text-right(:style="tagStyle")
                        .news-item-tag2(v-for='(tag, i) in item.tags' :key='item.tag' @click.stop="updateTag(tag)")
                            a.button.is-paddingless.is-small.is-uppercase.is-text
                                |{{tag}}
                                span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
                                span(v-else)    

CSS

/**
Using CSS GRID
*/

:root {
    --border-color: lightgray;
}

$grey: hsl(0, 0%, 48%);
$grey-light: hsl(0, 0%, 71%);
$size-7: 0.75rem;
$warning-light: hsl(48, 100%, 67%);

.news-item {
  display: grid;
  grid-template-areas:
    'time title title title title title title tags'
    'time likes dislikes bullish bearish comments . tags';
  grid-template-rows: 1fr auto;
  grid-template-columns: auto auto auto auto auto auto 1fr auto;
  border-bottom: 1px solid var(--border-color);
}

.news-item-title {
  grid-area: title;
    overflow: hidden;
}

.news-item-likes {
  grid-area: likes;
}

.news-item-dislikes {
  grid-area: dislikes;
}

.news-item-bullish {
  grid-area: bullish;
}

.news-item-bearish {
  grid-area: bearish;
}

.news-item-comments {
  grid-area: comments;
}

.news-item-tags {
  grid-area: tags;
    overflow: hidden;
}

a.news-item-tag {
  color: $grey;
  text-decoration: none;
}

.news-item-link {
  color: $grey;
  font-size: $size-7;
}

.news-item-pubdate {
  grid-area: time;
  align-self: center;
  color: $grey-light;
  font-size: $size-7;
}

.news-item-vote-icon {
  color: $grey-light;
  margin-right: 0.1rem;
}

.news-item-vote-count {
  font-size: 0.75rem;
  margin-right: 0.5rem;
  color: $grey-light;
}

.news-item-selected {
  background: $warning-light;
}

/** https://stackoverflow.com/questions/23608346/how-to-style-a-div-like-the-button-element */
.news-item {
  cursor: pointer;
  user-select: none;
}

a.button.is-paddingless.is-small.is-uppercase.is-text {
  text-decoration: none;
  line-height: 1.5;
  color: $grey;
}

/**
Using FLEXBOX
*/

.news-item2 {
    display: flex;
    align-items: center;
    border-bottom: 1px solid var(--border-color);
}

.news-item-content-wrapper {
    flex: 1;
    display: flex;
    flex-direction: column;
}

.news-item-votes-wrapper {
    display: flex;
}

.news-item-title2 {
    overflow: hidden;
}

.news-item-link2 {
  color: $grey;
  font-size: $size-7;
}

.news-item-pubdate2 {
  grid-area: time;
  align-self: center;
  color: $grey-light;
  font-size: $size-7;
}

.news-item-vote-icon2 {
  color: $grey-light;
  margin-right: 0.1rem;
}

.news-item-vote-count2 {
  font-size: 0.75rem;
  margin-right: 0.5rem;
  color: $grey-light;
}

.news-item-tags2 {
    overflow: hidden;
}

Vue JS

new Vue({
    el: "#__nuxt",
    data() {
        return {
            item: {
                feed_item_id: 1,
                title: 'The first one is made with a grid layout while the second one is made with a flex layout. The templates are different, how do I load the right one based on what is supported by the browser?',
                tags: ['trump', 'putin', 'defi'],
                likes: 10,
                dislikes: 5,
                bullish: 6,
                bearish: 0,
            },
            lineCountTag: 2,
            lineCountTitle: 2,
            heightPerLineTag: 30,
            heightPerLineTitle: 24,
        };
    },
    computed: {
        tagStyle() {
            return {
                height: this.heightPerLineTag * this.lineCountTag + "px",
                maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
            };
        },
        titleStyle() {
            return {
                height: this.heightPerLineTitle * this.lineCountTitle + "px",
                maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
            };
        }
    }
});
  • I have made 2 rows, first row is made with grid layout
  • second one uses flexbox layout
  • I want a Vue component which basically can load one of them depending on whether the browser supports flexbox or grid
  • The problem is the templates are different from each other
  • How do I do this?
PirateApp
  • 5,433
  • 4
  • 57
  • 90

1 Answers1

0

I think I got it, need 2 make 2 separate components, one with a flex and one with the css grid as the structure, cannot have both in one component because the structural layout of the item is different using css grid and flexbox, make a 3rd component that checks for support of CSS Grid in JS using a computed property CSS.supports('display', 'grid')

HTML

script#flex(type="text/x-template")
    .news-item2(
        :key="item.feed_item_id",
        :ref="item.feed_item_id",
        role="listitem",
        tabindex="0"
    )
        .news-item-pubdate-wrapper
            .news-item-pubdate2 1m  
        .news-item-content-wrapper
            .news-item-title-wrapper
                .news-item-title2(:style="titleStyle") {{ item.title }}
                    span.news-item-link2  example.com
            .news-item-votes-wrapper
                .news-item-likes2
                    span.icon.is-small.news-item-vote-icon2
                        i.fa.fa-xs.fa-thumbs-up
                    span.news-item-vote-count2 {{ item.likes }}
                .news-item-dislikes2
                    span.icon.is-small.news-item-vote-icon2
                        i.fa.fa-xs.fa-thumbs-down
                    span.news-item-vote-count2 {{ item.dislikes }}
                .news-item-bullish2
                    span.icon.is-small.news-item-vote-icon2
                        i.fa.fa-xs.fa-arrow-up
                    span.news-item-vote-count2 {{ item.bullish }}
                .news-item-bearish2
                    span.icon.is-small.news-item-vote-icon2
                        i.fa.fa-xs.fa-arrow-down
                    span.news-item-vote-count2 {{ item.bearish }}
                .news-item-comments2
                    span.icon.is-small.news-item-vote-icon2
                        i.fa.fa-xs.fa-comment-alt
                    span.news-item-vote-count2 0
        .news-item-tags-wrapper
            .news-item-tags2.has-text-right(:style="tagStyle")
                .news-item-tag2(
                    v-for="(tag, i) in item.tags",
                    :key="item.tag",
                    @click.stop="updateTag(tag)"
                )
                    a.button.is-paddingless.is-small.is-uppercase.is-text
                        | {{ tag }}
                        span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
                        span(v-else)    

script#grid(type="text/x-template")
    .news-item(
        :key="item.feed_item_id",
        :ref="item.feed_item_id",
        role="listitem",
        tabindex="0"
    )
        .news-item-pubdate 1m 
        .news-item-title(:style="titleStyle") {{ item.title }}
            span.news-item-link  example.com
        .news-item-likes
            span.icon.is-small.news-item-vote-icon
                i.fa.fa-xs.fa-thumbs-up
            span.news-item-vote-count {{ item.likes }}
        .news-item-dislikes
            span.icon.is-small.news-item-vote-icon
                i.fa.fa-xs.fa-thumbs-down
            span.news-item-vote-count {{ item.dislikes }}
        .news-item-bullish
            span.icon.is-small.news-item-vote-icon
                i.fa.fa-xs.fa-arrow-up
            span.news-item-vote-count {{ item.bullish }}
        .news-item-bearish
            span.icon.is-small.news-item-vote-icon
                i.fa.fa-xs.fa-arrow-down
            span.news-item-vote-count {{ item.bearish }}
        .news-item-comments
            span.icon.is-small.news-item-vote-icon
                i.fa.fa-xs.fa-comment-alt
            span.news-item-vote-count 0
        .news-item-tags.has-text-right(:style="tagStyle")
            .news-item-tag(
                v-for="(tag, i) in item.tags",
                :key="item.tag",
                @click.stop="updateTag(tag)"
            )
                a.button.is-paddingless.is-small.is-uppercase.is-text
                    | {{ tag }}
                    span(v-if="lineCountTag - 1 === i && item.tags.length > lineCountTag") ...
                    span(v-else)    

script#item(type="text/x-template")
    template(v-if="supportsCssGrid", :item="item")
        grid(:item="item")
    template(v-else)
        flex(:item="item")

body
    #__nuxt
        #__layout
            item(:item="item")

Vue JS

const lineClamp = {
    data() {
        return {
            lineCountTag: 2,
            lineCountTitle: 2,
            heightPerLineTag: 30,
            heightPerLineTitle: 24
        };
    },
    computed: {
        tagStyle() {
            return {
                height: this.heightPerLineTag * this.lineCountTag + "px",
                maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
            };
        },
        titleStyle() {
            return {
                height: this.heightPerLineTitle * this.lineCountTitle + "px",
                maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
            };
        }
    }
};

Vue.component("flex", {
    template: "#flex",
    mixins: [lineClamp],
    props: {
        item: {
            type: Object,
            default: () => {},
            required: true
        }
    }
});

Vue.component("grid", {
    template: "#grid",
    mixins: [lineClamp],
    props: {
        item: {
            type: Object,
            default: () => {},
            required: true
        }
    }
});

Vue.component("item", {
    template: "#item",
    props: {
        item: {
            type: Object,
            default: () => {},
            required: true
        }
    },
    computed: {
        supportsCssGrid() {
            return CSS.supports("display", "grid");
        }
    }
});

new Vue({
    el: "#__nuxt",
    data() {
        return {
            item: {
                feed_item_id: 1,
                title:
                    "The first one is made with a grid layout while the second one is made with a flex layout. The templates are different, how do I load the right one based on what is supported by the browser?",
                tags: ["trump", "putin", "defi"],
                likes: 10,
                dislikes: 5,
                bullish: 6,
                bearish: 0
            },
            lineCountTag: 2,
            lineCountTitle: 2,
            heightPerLineTag: 30,
            heightPerLineTitle: 24
        };
    },
    computed: {
        tagStyle() {
            return {
                height: this.heightPerLineTag * this.lineCountTag + "px",
                maxHeight: this.heightPerLineTag * this.lineCountTag + "px"
            };
        },
        titleStyle() {
            return {
                height: this.heightPerLineTitle * this.lineCountTitle + "px",
                maxHeight: this.heightPerLineTitle * this.lineCountTitle + "px"
            };
        }
    }
});
PirateApp
  • 5,433
  • 4
  • 57
  • 90