Как сделать первый столбец одинакового размера в каждой сетке CSS? - PullRequest
1 голос
/ 01 октября 2019

У меня есть элементы, которые динамически добавляются, где каждый элемент использует CSS Grid Layout. Вот как выглядит моя сетка

enter image description here

Как видно, первый столбец в первой сетке не совпадает с шириной первого столбца ввторая сетка. Как мне сделать их одинаковыми?

Код на Codepen

HTML

<div id="app">
  <div class="list_item" v-for="item in sortedItems" :key="item.feedItemId">
    <div class="news_time">{{getFormattedDate(item.pubdate)}}</div>
    <div class="news_title">{{item.title}}</div>
    <div class="news_link"><span class="news_link_text">{{getHostFromUrl(item.link)}}</span></div>
    <div class="news_stats" :class="isStatShown(item) ? '' : 'hide'">
      <div class="likes"><i class="fa fa-thumbs-up"></i><span class="stats_text">{{item.likes || 0}}</span></div>
      <div class="dislikes"><i class="fa fa-thumbs-down"></i><span class="stats_text">{{item.dislikes || 0}}</span></div>
      <div class="bullish"><i class="fa fa-arrow-up"></i><span class="stats_text">{{item.bullish || 0}}</span></div>
      <div class="bearish"><i class="fa fa-arrow-down"></i><span class="stats_text">{{item.bearish || 0}}</span></div>
      <div class="comments"><i class="fa fa-comment-alt"></i><span class="stats_text">{{item.comments || 0}}</span></div>
    </div>
    <div class="news_tags">
      <div class="news_tag" v-for="tag in item.tags" :key="item.tag">{{tag}}</div>
    </div>
  </div>
</div>

CSS

@import url('https://fonts.googleapis.com/css?family=Noto+Sans&display=swap');

/**
There are 2 CSS Grids to acheive this layout?
It can be achieved in 1 layout so why 2?
Because stats are shown conditionally.
Meaning if all are 0 we dont show the whole row!

The outer CSS Grid has 3 rows and 4 columns
Row 1
time title title and tag
Row 2
time link link and tag
Row 3
time stats <empty-cell> and tag

The row containing stats is set to auto as it collapses when all stats are 0
*/

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  line-height: 1.6;
  font-family: 'Noto Sans', sans-serif;
}

.news_time {
  grid-area: time;
}

.news_title {
  grid-area: title;
}

.news_link {
  grid-area: link;
}

.news_stats {
  grid-area: stats;
}

.news_tags {
  grid-area: tags;
}

.list_item {
  display: grid;
  grid-template-areas:
    "time title title tags"
    "time link link tags"
    "time stats . tags";
  grid-template-rows: 1fr auto auto;
  grid-template-columns: auto auto 1fr auto;
  align-items: center;
}

.likes {
  grid-area: likes;
}

.dislikes {
  grid-area: dislikes;
}

.bullish {
  grid-area: bullish;
}

.bearish {
  grid-area: bearish;
}

.comments {
  grid-area: comments;
}

/**
This is our second CSS Grid
If stats are not available, this whole grid will be hidden.
It has only 1 row and 5 columns
Row 1
Likes Dislikes Bullish Bearish and Comments
It adds a gap to separate the adjacent cells
*/

.news_stats {
  display: grid;
  grid-template-areas:
    "likes dislikes bullish bearish comments";
  grid-rows: 1fr;
  grid-columns: 1fr 1fr 1fr 1fr 1fr;
  grid-gap: 0.75rem;
  gap: 0.75rem;
}

/**
Spacing
*/

.list_item {
  border: 1px solid #eef;
}

.news_stats {
  font-size: 0.75rem;
  opacity: 0.4;
  margin: 0.3rem 0;
}

.news_link {
  font-size: 0.85rem;
  opacity: 0.6;
}

.stats_text {
  display: inline-block;
  margin-left: 0.3rem;
}

.news_time {
  font-size: 0.75rem;
  margin: 1rem;
  opacity: 0.75;
}

.news_tags {
  margin: 1rem;
  font-size: 0.75rem;
  opacity: 0.75;
}

/**
Use this class to hide anything that must not be shown
*/
.hide {
  display: none;
}

JS

const mapState = Vuex.mapState;
const mapMutations = Vuex.mapMutations;
const mapGetters = Vuex.mapGetters;

const storeObject = {
  state() {
    return {
      items: {
        "54069cf1-7a6a-d356-2a7a-3a4d5cf82191": {
          feedItemId: "54069cf1-7a6a-d356-2a7a-3a4d5cf82191",
          pubdate: new Date("2019-10-1 16:06:40+05:30"),
          link:
            "https://www.cryptovibes.com/blog/2019/09/30/cardano-technology-will-be-used-by-new-balance-to-combat-counterfeits/",
          title:
            "Cardano Technology will be Used by New Balance to Combat Counterfeits",
          author: "Ali Raza",
          feed_id: 98,
          likes: 3,
          dislikes: 1,
          bullish: 6,
          bearish: 3,
          comments: 5,
          tags: ["ADA"]
        },
        "c68ef754-da41-b4fd-eb3a-150735fc0535": {
          feedItemId: "c68ef754-da41-b4fd-eb3a-150735fc0535",
          pubdate: new Date("2019-09-30 02:50:43+05:30"),
          link:
            "https://ethereumworldnews.com/jp-morgan-bitcoin-price-crash-8000/",
          title: "Top Airdrops You Should Look Out for in October 2019",
          author: "Ogwu Osaemezu Emmanuel",
          feed_id: 98,
          tags: ["BTC", "ETH"]
        },
        "a68ef754-da41-b4fd-eb3a-150735fc0535": {
          feedItemId: "a68ef754-da41-b4fd-eb3a-150735fc0535",
          pubdate: new Date("2019-09-30 02:50:43+05:30"),
          link:
            "https://ethereumworldnews.com/jp-morgan-bitcoin-price-crash-8000/",
          title: "JP Morgan on What Caused Bitcoin Price Crash to $8,000",
          author: "Nick Chong",
          feed_id: 98,
          likes: 0,
          dislikes: 8,
          bullish: 0,
          bearish: 3,
          comments: 8,
          tags: ["BTC", "ETH", "EOS"]
        }
      }
    };
  },

  getters: {
    sortedItems(state) {
      return Object.values(state.items).sort((a, b) => b.pubdate - a.pubdate);
    }
  }
};

const store = new Vuex.Store(storeObject);

const timeSince = Vue.mixin({
  methods: {
    timeSince(date) {
      var seconds = Math.floor((new Date() - date) / 1000);

      var interval = Math.floor(seconds / 31536000);

      if (interval > 1) {
        return interval + " y";
      }
      interval = Math.floor(seconds / 2592000);
      if (interval > 1) {
        return interval + " M";
      }
      interval = Math.floor(seconds / 86400);
      if (interval > 1) {
        return interval + " d";
      }
      interval = Math.floor(seconds / 3600);
      if (interval > 1) {
        return interval + " h";
      }
      interval = Math.floor(seconds / 60);
      if (interval > 1) {
        return interval + " m";
      }
      return Math.floor(seconds) + " s";
    }
  }
});

const viewModel = new Vue({
  el: "#app",
  mixins: [timeSince],
  computed: {
    ...mapState(["items"]),
    ...mapGetters(["sortedItems"])
  },
  methods: {
    getHostFromUrl(url) {
      return new URL(url).host.replace(/^www\./, "");
    },

    getFormattedDate(date) {
      return this.timeSince(date);
    },

    isStatShown(item) {
      return item.likes || item.dislikes || item.bullish || item.bearish || item.comments;
    }
  },
  store
});

1 Ответ

1 голос
/ 01 октября 2019

Вы можете сделать это так:

.news_time {
    font-size: 0.75rem;
    margin: 1rem;
    opacity: 0.75;
    width: 2rem;
}
...