-1

I'm working on a vue.js / nuxt.js project, applying the Atomic Design methodology, I need to do one to set the grid layout and using CSS Grid Layout.

I already did the component

GridLayout.vue

<template>
  <div class="grid">
    <style>
    {{ css }}
    </style>
    <slot />
  </div>
</template>

<script>
export default {
  props: {
    columns: String,
    rows: String,
    areas: String,
    gap: String,
    columnGap: String,
    rowGap: String,
    // breakpoints
    small: Object,
    medium: Object,
    large: Object
  },
  computed: {
    css () {
      let large = ''
      let finalStyle = ''

      // generic
      let generic = ''
      if (this.columns) generic += `grid-template-columns: ${this.columns};`
      if (this.rows) generic += `grid-template-rows: ${this.rows};`
      if (this.areas) generic += `grid-template-areas: "${this.areas}";`
      if (this.gap) generic += `grid-gap: ${this.gap};`
      if (this.columnGap) generic += `grid-column-gap: ${this.columnGap};`
      if (this.rowGap) generic += `grid-row-gap: ${this.rowGap};`
      finalStyle += ` .grid { ${generic} }`

      // small
      let small = ''
      if (this.small) {
        if (this.small.columns) small += `grid-template-columns: ${this.small.columns};`
        if (this.small.rows) small += `grid-template-rows: ${this.small.rows};`
        if (this.small.areas) small += `grid-template-areas: "${this.small.areas}";`
        if (this.small.gap) small += `grid-gap: ${this.small.gap};`
        if (this.small.columnGap) small += `grid-column-gap: ${this.small.columnGap};`
        if (this.small.rowGap) small += `grid-row-gap: ${this.small.rowGap};`
        finalStyle += `@media (max-width: 600px) { .grid { ${small} } } `
      }

      // medium
      let medium = ''
      if (this.medium) {
        if (this.medium.columns) medium += `grid-template-columns: ${this.medium.columns};`
        if (this.medium.rows) medium += `grid-template-rows: ${this.medium.rows};`
        if (this.medium.areas) medium += `grid-template-areas: "${this.medium.areas}";`
        if (this.medium.gap) medium += `grid-gap: ${this.medium.gap};`
        if (this.medium.columnGap) medium += `grid-column-gap: ${this.medium.columnGap};`
        if (this.medium.rowGap) medium += `grid-row-gap: ${this.medium.rowGap};`
        finalStyle += `@media (min-width: 600px) and (max-width: 992px) { .grid { ${medium} } } `
      }

      return finalStyle
    },
  },
}
</script>

<style lang="scss" scoped>
.grid {
  display: grid;
}
</style>

using component on any page.vue

<template>
  <GridLayout
    columns="1fr 1fr 1fr 1fr"
    rows="auto"
    gap="10px"
    :medium="{
      columns: '1fr 1fr',
      rows:'auto auto'
    }"
    :small="{
      columns: '1fr',
      rows: 'auto auto auto auto',
    }"
  >
    <h1>1</h1>
    <h1>2</h1>
    <h1>3</h1>
    <h1>3</h1>
  </GridLayout>
</template>

<script>
import { GridLayout } from '@/components/bosons'

export default {
  components: {
    GridLayout
  }
}
</script>

problems

1 - the style tag <style> inside <template> needs to be scoped, applying only in the component itself

2 - whenever I want new properties for the GridLayout component, for example, child align, I will have to add everywhere in computed, that is, generic, small, medium, large

how could I solve these problems? And maybe letting my code smaller, smarter, less repetitive

thanks in advance

Community
  • 1
  • 1
Yung Silva
  • 1,324
  • 4
  • 20
  • 40
  • I think this question is more appropriate for Code Review: https://codereview.stackexchange.com. – chipit24 Jul 14 '19 at 22:12
  • See https://stackoverflow.com/help/dont-ask. Can you edit your question to focus on a specific coding problem and not about opinion? – chipit24 Jul 14 '19 at 22:15
  • @chipit24, I updated my question, I do not ask for more opinion and I put the problems explicitly. – Yung Silva Jul 14 '19 at 22:22
  • 1
    1) If your styles should only apply to a single node, you'll need an `id`. I don't know vue very well, but if it has a state, you could create a random id when the instance is generated and store it. and 2) do you know what is really good at repetitive work: functions or loops \*wink\* – Thomas Jul 14 '19 at 22:50
  • Vue is not only compatible with Atomic Design but it is, in fact, based on the same principles. You just need to follow Vue's specific syntax and conventions. In Vue, the scoped CSS sits outside of the markup and it is scoped. Read [the docs](https://vue-loader.vuejs.org/guide/scoped-css.html). I answered a question yesterday about scoped CSS in Vue for which I made this [codesandbox](https://codesandbox.io/s/vue-template-i0v83). You might find it useful. – tao Jul 15 '19 at 00:12
  • @Thomas, scoping is more than that and the `id`won't help. Scoping is also about applying style to the component **without** applying it to sub-components, unless the `>>>` (deep) combinator is used. – tao Jul 15 '19 at 00:20

1 Answers1

0
  1. Placing a style tag in a template produces a warning; the warning explains why it should be avoided:

Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <style>, as they will not be parsed.

You have an SFC (single file component) which already defines a scoped styles block–use that for any pre-defined styles. For your dynamically generated styles you will have to use the style binding, for example: html <div class="grid" :style="css"> <slot /> </div> ... computed: { css () { let generic = {} if (this.columns) generic['grid-template-columns'] = this.columns; if (this.columns) generic['grid-template-rows'] = this.rows; ... return [generic, large, mediumn, small];

You can read more about this in the Vue docs here: https://v2.vuejs.org/v2/guide/class-and-style.html#Binding-Inline-Styles.

You can also look into vue-styled-components: https://github.com/styled-components/vue-styled-components. I've only used it for React, but I'd assume it might be just as useful to use for your situation in Vue.

  1. Extract the repeated code into a function, and call that code as many times as you need. This is called making your code DRY. For example:

    methods: {
      generateGridStyles(options) {
        return {
          'grid-template-columns': options.columns || this.columns,
          'grid-template-rows': options.rows || this.rows,
          'grid-template-areas': options.areas || this.areas,
          'grid-gap': options.gap || this.gap,
          'grid-column-gap': options.columnGap || this.columnGap,
          'grid-row-gap': options.rowGap || this.rowGap,
        }
      }
    }
    

This is how that function may be used: js let small; if (this.small) { small = generateGridStyles(this.small); }

Abstractions come with an overhead: another component to maintain and reason about. Think about your overall design and what the goal of a GridLayout component is.

In my opinion, CSS grid layout is already fairly straightforward and your component seems to simply pass through the CSS grid properties and map them into a wrapper; it doesn't abstract away any of the work of using CSS grid. I do not see the benefit of this when it will be easier to maintain and read the "raw" CSS grid styles and use media queries to adjust styles.

tony19
  • 125,647
  • 18
  • 229
  • 307
chipit24
  • 6,509
  • 7
  • 47
  • 67
  • A beautiful organized, thank you very much for responding. But I see a problem in your code, or one more difficulty on my part .. How to set and pass media queries to `:style="css"`? If you showed me I would be very happy. – Yung Silva Jul 15 '19 at 00:21
  • You could get it programatically using `Window.matchMedia`: https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia. See the related SO question here: https://stackoverflow.com/questions/31511001/is-there-a-javascript-equivalent-to-using-media-query. Then modify your `css` object based on those results. – chipit24 Jul 15 '19 at 00:35
  • all props that are not within breakpoints, `small`, `medium` or `large`, are generic, so how do I mount the generic style object? [see an image of my current code](https://prnt.sc/of1h7f) – Yung Silva Jul 15 '19 at 02:19
  • I've updated the example in my answer. To get the generic styles you probably don't want to wrap it in an `if` block, and you can (if you use the updated code) simply have `generic = generateGridStyles()`. Though "generic" may not be the wording you want–it sounds more like these are default or fallback values. – chipit24 Jul 15 '19 at 04:39
  • when i call `generic = generateGridStyles()` i get the error `Cannot read property 'columns' of undefined` in options.columns – Yung Silva Jul 15 '19 at 04:53
  • but this your way of thinking does not seem right, and when `generateGridStyles () ` receives an `option`, but does not have for example `options.gap`? it will get `this.gap`, but `this.gap` is only to be `standard/generic` style – Yung Silva Jul 15 '19 at 04:56
  • Dude... I explained myself clearly I'm answering your question based on the scant information you gave me. It seems you don't even know what you want. – chipit24 Jul 15 '19 at 13:43
  • what you mean? in the scope of my question it clearly shows that I need a `generic / default` style and `breackpoints`... in your code, the `generateGridStyles (options)` function will return `generic/default` style for all breackpints when there are no `options.xxxx` – Yung Silva Jul 15 '19 at 14:01