Skip to content
Permalink
Browse files

feat(b-row): add Bootstrap v4.4 row columns support (#4439)

* feat(b-row): add Bootstrap v4.4 row columns support

* Update row.js

* Update row.js

* Update row.js

* Update row.spec.js

* Update row.spec.js

* Update row.js

* Update README.md

* Update package.json

* Update package.json

* Update col.js

* Update col.js

* Update package.json

* Minor tweaks

* Update row.js

* Update package.json

* minor adjustments to row columns excamples
  • Loading branch information
tmorehouse authored and jackmu95 committed Dec 2, 2019
1 parent ee1c10e commit 833b028a2d6101d01b7012a7378359db1c801695
@@ -70,11 +70,10 @@ like the

## Containers `<b-container>`

Containers (`<b-container>`) are the most basic layout element in Bootstrap and is **required when
using the grid system**. Choose from a responsive, fixed-width container (meaning its max-width
changes at each breakpoint) by default, or fluid-width (meaning it's 100% wide all the time) by
setting 'fluid' prop, or responsive containers where the container is fluid up until a specific
breakpoint.
Containers (`<b-container>`) are the most basic layout element in Bootstrap. Choose from a
responsive, fixed-width container (meaning its max-width changes at each breakpoint) by default, or
fluid-width (meaning it's 100% wide all the time) by setting 'fluid' prop, or responsive containers
where the container is fluid up until a specific breakpoint (requires Bootstrap CSS `v4.4+`).

While containers can be nested, most layouts do not require a nested container.

@@ -85,7 +84,7 @@ The default breakpoint widths can be configured using Bootstrap V4.x SCSS variab
### Default container

The default `<b-container>` is a responsive, fixed-width container, meaning its `max-width` changes
at each breakpoint.
at each viewport width breakpoint.

```html
<b-container>
@@ -109,7 +108,7 @@ Setting the `fluid` prop to true (or an empty string) is equivalent to the Boots

### Responsive fluid containers

<span class="badge badge-warning small">Requires Bootstrap v4.4+ CSS</span>
<span class="badge badge-info small">Requires Bootstrap v4.4+ CSS</span>

Responsive containers are new in Bootstrap v4.4. They allow you to specify a container that is 100%
wide (fluid) until particular breakpoint is reached at which point a `max-width` is applied. For
@@ -137,8 +136,9 @@ Setting the fluid prop to a breakpoint name translates to the Bootstrap class

## Rows `<b-row>` and `<b-form-row>`

`<b-row>` components should be placed inside a `<b-container>` component, or an element (such as a
`<div>`) that has the class `container` or `container-fluid` applied to it.
Rows are wrappers for [columns](#columns-b-col). Each column has horizontal padding (called a
gutter) for controlling the space between them. This padding is then counteracted on the rows with
negative margins. This way, all the content in your columns is visually aligned down the left side.

You can remove the margin from `<b-row>` and padding from `<b-col>` by setting the `no-gutters` prop
on `<b-row>`.
@@ -622,4 +622,83 @@ within an existing `<b-col>` component. Nested rows should include a set of colu
<!-- b-grid-nesting.vue -->
```

## Row columns

<span class="badge badge-info small">Requires Bootstrap v4.4+ CSS</span>

Use the responsive `cols-*` props in `<b-row>` to quickly set the number of columns that best render
your content and layout. Whereas normal column widths are apply to the individual `<b-col>` columns
(e.g., `<b-col md="4">`), the row columns `col-*` props are set on the parent `<b-row>` as a
shortcut.

Use these row columns to quickly create basic grid layouts or to control your card layouts. The
default maximum number of row columns in Bootstrap v4.4 is `6` (unlike the regular columns which
have a default maximum of `12` columns)

The value specified in the `<b-row>` prop(s) is the number of columns to create per row (whereas the
props on `<b-col>` refer to the number of columns to occupy).

```html
<b-container class="bv-example-row mb-3">
<b-row cols="2">
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
</b-row>
</b-container>

<b-container class="bv-example-row mb-3">
<b-row cols="3">
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
</b-row>
</b-container>

<b-container class="bv-example-row mb-3">
<b-row cols="4">
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
</b-row>
</b-container>

<b-container class="bv-example-row">
<b-row cols="4">
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col cols="6">Column</b-col>
<b-col>Column</b-col>
</b-row>
</b-container>

<!-- b-grid-row-cols-introduction.vue -->
```

You can control the number of columns at each breakpoint level via the following `<b-row>` props:

- `cols` for `xs` and up screens
- `cols-sm` for `sm` and up screens
- `cols-md` for `md` and up screens
- `cols-lg` for `lg` and up screens
- `cols-xl` for `xl` and up screens

```html
<b-container class="bv-example-row">
<b-row cols="1" cols-sm="2" cols-md="4" cols-lg="6">
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
<b-col>Column</b-col>
</b-row>
</b-container>

<!-- b-grid-row-cols-breakpoints.vue -->
```

<!-- Component reference added automatically from component package.json -->
@@ -3,9 +3,10 @@ import identity from '../../utils/identity'
import memoize from '../../utils/memoize'
import suffixPropName from '../../utils/suffix-prop-name'
import { arrayIncludes } from '../../utils/array'
import { isUndefinedOrNull } from '../../utils/inspect'
import { keys, assign, create } from '../../utils/object'
import { getBreakpointsUpCached } from '../../utils/config'
import { isUndefinedOrNull } from '../../utils/inspect'
import { assign, create, keys } from '../../utils/object'
import { lowerCase } from '../../utils/string'

const RX_COL_CLASS = /^col-/

@@ -35,11 +36,11 @@ const computeBreakpoint = (type, breakpoint, val) => {
// Since the default is false, an empty string indicates the prop's presence.
if (type === 'col' && (val === '' || val === true)) {
// .col-md
return className.toLowerCase()
return lowerCase(className)
}
// .order-md-6
className += `-${val}`
return className.toLowerCase()
return lowerCase(className)
}

// Memoized function for better performance on generating class names
@@ -11,7 +11,7 @@
"props": [
{
"prop": "fluid",
"description": "When set to true, makes the row 100% wide all the time, or set to one of the Bootstrap breakpoint names for 100% width up to the breakpoint"
"description": "When set to true, makes the row 100% wide all the time, or set to one of the Bootstrap breakpoint names for 100% width up to the breakpoint (requires Bootstrap v4.4+ CSS for breakpoint specific value)"
}
]
},
@@ -33,6 +33,31 @@
{
"prop": "alignContent",
"description": "Align columns items together on the cross axis: 'start', 'center', 'end', 'around', 'between' or 'stretch'. Has no effect on single rows of items"
},
{
"prop": "cols",
"version": "2.2.0",
"description": "The number row columns to create at the 'xs' breakpoint. Requires Bootstrap v4.4 CSS"
},
{
"prop": "colsSm",
"version": "2.2.0",
"description": "The number row columns to create at the 'sm' breakpoint. Requires Bootstrap v4.4 CSS"
},
{
"prop": "colsMd",
"version": "2.2.0",
"description": "The number row columns to create at the 'md' breakpoint. Requires Bootstrap v4.4 CSS"
},
{
"prop": "colsLg",
"version": "2.2.0",
"description": "The number row columns to create at the 'lg' breakpoint. Requires Bootstrap v4.4 CSS"
},
{
"prop": "colsXl",
"version": "2.2.0",
"description": "The number row columns to create at the 'xl' breakpoint. Requires Bootstrap v4.4 CSS"
}
]
},
@@ -1,53 +1,110 @@
import Vue from '../../utils/vue'
import { mergeData } from 'vue-functional-data-merge'
import identity from '../../utils/identity'
import memoize from '../../utils/memoize'
import suffixPropName from '../../utils/suffix-prop-name'
import { arrayIncludes } from '../../utils/array'
import { getBreakpointsUpCached } from '../../utils/config'
import { create, keys } from '../../utils/object'
import { lowerCase, toString, trim } from '../../utils/string'

const COMMON_ALIGNMENT = ['start', 'end', 'center']

export const props = {
tag: {
type: String,
default: 'div'
},
noGutters: {
type: Boolean,
default: false
},
alignV: {
type: String,
default: null,
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['baseline', 'stretch']), str)
},
alignH: {
type: String,
default: null,
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around']), str)
},
alignContent: {
type: String,
default: null,
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around', 'stretch']), str)
// Generates a prop object with a type of `[String, Number]`
const strNum = () => ({
type: [String, Number],
default: null
})

// Compute a `row-cols-{breakpoint}-{cols}` class name
// Memoized function for better performance on generating class names
const computeRowColsClass = memoize((breakpoint, cols) => {
cols = trim(toString(cols))
return cols ? lowerCase(['row-cols', breakpoint, cols].filter(identity).join('-')) : null
})

// Get the breakpoint name from the `rowCols` prop name
// Memoized function for better performance on extracting breakpoint names
const computeRowColsBreakpoint = memoize(prop => lowerCase(prop.replace('cols', '')))

// Cached copy of the `row-cols` breakpoint prop names
// Will be populated when the props are generated
let rowColsPropList = []

// Lazy evaled props factory for <b-row> (called only once,
// the first time the component is used)
const generateProps = () => {
// Grab the breakpoints from the cached config (including the '' (xs) breakpoint)
const breakpoints = getBreakpointsUpCached()

// Supports classes like: `row-cols-2`, `row-cols-md-4`, `row-cols-xl-6`
const rowColsProps = breakpoints.reduce((props, breakpoint) => {
props[suffixPropName(breakpoint, 'cols')] = strNum()
return props
}, create(null))

// Cache the row-cols prop names
rowColsPropList = keys(rowColsProps)

// Return the generated props
return {
tag: {
type: String,
default: 'div'
},
noGutters: {
type: Boolean,
default: false
},
alignV: {
type: String,
default: null,
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['baseline', 'stretch']), str)
},
alignH: {
type: String,
default: null,
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around']), str)
},
alignContent: {
type: String,
default: null,
validator: str =>
arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around', 'stretch']), str)
},
...rowColsProps
}
}

// We do not use `Vue.extend()` here as that would evaluate the props
// immediately, which we do not want to happen
// @vue/component
export const BRow = /*#__PURE__*/ Vue.extend({
export const BRow = {
name: 'BRow',
functional: true,
props,
get props() {
// Allow props to be lazy evaled on first access and
// then they become a non-getter afterwards
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters
delete this.props
this.props = generateProps()
return this.props
},
render(h, { props, data, children }) {
return h(
props.tag,
mergeData(data, {
staticClass: 'row',
class: {
'no-gutters': props.noGutters,
[`align-items-${props.alignV}`]: props.alignV,
[`justify-content-${props.alignH}`]: props.alignH,
[`align-content-${props.alignContent}`]: props.alignContent
}
}),
children
)
const classList = []
// Loop through row-cols breakpoint props and generate the classes
rowColsPropList.forEach(prop => {
const c = computeRowColsClass(computeRowColsBreakpoint(prop), props[prop])
// If a class is returned, push it onto the array
if (c) {
classList.push(c)
}
})
classList.push({
'no-gutters': props.noGutters,
[`align-items-${props.alignV}`]: props.alignV,
[`justify-content-${props.alignH}`]: props.alignH,
[`align-content-${props.alignContent}`]: props.alignContent
})
return h(props.tag, mergeData(data, { staticClass: 'row', class: classList }), children)
}
})
}

0 comments on commit 833b028

Please sign in to comment.
You can’t perform that action at this time.