Skip to content

Commit 833b028

Browse files
tmorehousejacobmllr95
authored andcommitted
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
1 parent ee1c10e commit 833b028

File tree

5 files changed

+263
-54
lines changed

5 files changed

+263
-54
lines changed

src/components/layout/README.md

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,10 @@ like the
7070

7171
## Containers `<b-container>`
7272

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

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

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

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

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

110109
### Responsive fluid containers
111110

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

114113
Responsive containers are new in Bootstrap v4.4. They allow you to specify a container that is 100%
115114
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
137136

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

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

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

625+
## Row columns
626+
627+
<span class="badge badge-info small">Requires Bootstrap v4.4+ CSS</span>
628+
629+
Use the responsive `cols-*` props in `<b-row>` to quickly set the number of columns that best render
630+
your content and layout. Whereas normal column widths are apply to the individual `<b-col>` columns
631+
(e.g., `<b-col md="4">`), the row columns `col-*` props are set on the parent `<b-row>` as a
632+
shortcut.
633+
634+
Use these row columns to quickly create basic grid layouts or to control your card layouts. The
635+
default maximum number of row columns in Bootstrap v4.4 is `6` (unlike the regular columns which
636+
have a default maximum of `12` columns)
637+
638+
The value specified in the `<b-row>` prop(s) is the number of columns to create per row (whereas the
639+
props on `<b-col>` refer to the number of columns to occupy).
640+
641+
```html
642+
<b-container class="bv-example-row mb-3">
643+
<b-row cols="2">
644+
<b-col>Column</b-col>
645+
<b-col>Column</b-col>
646+
<b-col>Column</b-col>
647+
<b-col>Column</b-col>
648+
</b-row>
649+
</b-container>
650+
651+
<b-container class="bv-example-row mb-3">
652+
<b-row cols="3">
653+
<b-col>Column</b-col>
654+
<b-col>Column</b-col>
655+
<b-col>Column</b-col>
656+
<b-col>Column</b-col>
657+
</b-row>
658+
</b-container>
659+
660+
<b-container class="bv-example-row mb-3">
661+
<b-row cols="4">
662+
<b-col>Column</b-col>
663+
<b-col>Column</b-col>
664+
<b-col>Column</b-col>
665+
<b-col>Column</b-col>
666+
</b-row>
667+
</b-container>
668+
669+
<b-container class="bv-example-row">
670+
<b-row cols="4">
671+
<b-col>Column</b-col>
672+
<b-col>Column</b-col>
673+
<b-col cols="6">Column</b-col>
674+
<b-col>Column</b-col>
675+
</b-row>
676+
</b-container>
677+
678+
<!-- b-grid-row-cols-introduction.vue -->
679+
```
680+
681+
You can control the number of columns at each breakpoint level via the following `<b-row>` props:
682+
683+
- `cols` for `xs` and up screens
684+
- `cols-sm` for `sm` and up screens
685+
- `cols-md` for `md` and up screens
686+
- `cols-lg` for `lg` and up screens
687+
- `cols-xl` for `xl` and up screens
688+
689+
```html
690+
<b-container class="bv-example-row">
691+
<b-row cols="1" cols-sm="2" cols-md="4" cols-lg="6">
692+
<b-col>Column</b-col>
693+
<b-col>Column</b-col>
694+
<b-col>Column</b-col>
695+
<b-col>Column</b-col>
696+
<b-col>Column</b-col>
697+
<b-col>Column</b-col>
698+
</b-row>
699+
</b-container>
700+
701+
<!-- b-grid-row-cols-breakpoints.vue -->
702+
```
703+
625704
<!-- Component reference added automatically from component package.json -->

src/components/layout/col.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import identity from '../../utils/identity'
33
import memoize from '../../utils/memoize'
44
import suffixPropName from '../../utils/suffix-prop-name'
55
import { arrayIncludes } from '../../utils/array'
6-
import { isUndefinedOrNull } from '../../utils/inspect'
7-
import { keys, assign, create } from '../../utils/object'
86
import { getBreakpointsUpCached } from '../../utils/config'
7+
import { isUndefinedOrNull } from '../../utils/inspect'
8+
import { assign, create, keys } from '../../utils/object'
9+
import { lowerCase } from '../../utils/string'
910

1011
const RX_COL_CLASS = /^col-/
1112

@@ -35,11 +36,11 @@ const computeBreakpoint = (type, breakpoint, val) => {
3536
// Since the default is false, an empty string indicates the prop's presence.
3637
if (type === 'col' && (val === '' || val === true)) {
3738
// .col-md
38-
return className.toLowerCase()
39+
return lowerCase(className)
3940
}
4041
// .order-md-6
4142
className += `-${val}`
42-
return className.toLowerCase()
43+
return lowerCase(className)
4344
}
4445

4546
// Memoized function for better performance on generating class names

src/components/layout/package.json

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"props": [
1212
{
1313
"prop": "fluid",
14-
"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"
14+
"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)"
1515
}
1616
]
1717
},
@@ -33,6 +33,31 @@
3333
{
3434
"prop": "alignContent",
3535
"description": "Align columns items together on the cross axis: 'start', 'center', 'end', 'around', 'between' or 'stretch'. Has no effect on single rows of items"
36+
},
37+
{
38+
"prop": "cols",
39+
"version": "2.2.0",
40+
"description": "The number row columns to create at the 'xs' breakpoint. Requires Bootstrap v4.4 CSS"
41+
},
42+
{
43+
"prop": "colsSm",
44+
"version": "2.2.0",
45+
"description": "The number row columns to create at the 'sm' breakpoint. Requires Bootstrap v4.4 CSS"
46+
},
47+
{
48+
"prop": "colsMd",
49+
"version": "2.2.0",
50+
"description": "The number row columns to create at the 'md' breakpoint. Requires Bootstrap v4.4 CSS"
51+
},
52+
{
53+
"prop": "colsLg",
54+
"version": "2.2.0",
55+
"description": "The number row columns to create at the 'lg' breakpoint. Requires Bootstrap v4.4 CSS"
56+
},
57+
{
58+
"prop": "colsXl",
59+
"version": "2.2.0",
60+
"description": "The number row columns to create at the 'xl' breakpoint. Requires Bootstrap v4.4 CSS"
3661
}
3762
]
3863
},

src/components/layout/row.js

Lines changed: 97 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,110 @@
1-
import Vue from '../../utils/vue'
21
import { mergeData } from 'vue-functional-data-merge'
2+
import identity from '../../utils/identity'
3+
import memoize from '../../utils/memoize'
4+
import suffixPropName from '../../utils/suffix-prop-name'
35
import { arrayIncludes } from '../../utils/array'
6+
import { getBreakpointsUpCached } from '../../utils/config'
7+
import { create, keys } from '../../utils/object'
8+
import { lowerCase, toString, trim } from '../../utils/string'
49

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

7-
export const props = {
8-
tag: {
9-
type: String,
10-
default: 'div'
11-
},
12-
noGutters: {
13-
type: Boolean,
14-
default: false
15-
},
16-
alignV: {
17-
type: String,
18-
default: null,
19-
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['baseline', 'stretch']), str)
20-
},
21-
alignH: {
22-
type: String,
23-
default: null,
24-
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around']), str)
25-
},
26-
alignContent: {
27-
type: String,
28-
default: null,
29-
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around', 'stretch']), str)
12+
// Generates a prop object with a type of `[String, Number]`
13+
const strNum = () => ({
14+
type: [String, Number],
15+
default: null
16+
})
17+
18+
// Compute a `row-cols-{breakpoint}-{cols}` class name
19+
// Memoized function for better performance on generating class names
20+
const computeRowColsClass = memoize((breakpoint, cols) => {
21+
cols = trim(toString(cols))
22+
return cols ? lowerCase(['row-cols', breakpoint, cols].filter(identity).join('-')) : null
23+
})
24+
25+
// Get the breakpoint name from the `rowCols` prop name
26+
// Memoized function for better performance on extracting breakpoint names
27+
const computeRowColsBreakpoint = memoize(prop => lowerCase(prop.replace('cols', '')))
28+
29+
// Cached copy of the `row-cols` breakpoint prop names
30+
// Will be populated when the props are generated
31+
let rowColsPropList = []
32+
33+
// Lazy evaled props factory for <b-row> (called only once,
34+
// the first time the component is used)
35+
const generateProps = () => {
36+
// Grab the breakpoints from the cached config (including the '' (xs) breakpoint)
37+
const breakpoints = getBreakpointsUpCached()
38+
39+
// Supports classes like: `row-cols-2`, `row-cols-md-4`, `row-cols-xl-6`
40+
const rowColsProps = breakpoints.reduce((props, breakpoint) => {
41+
props[suffixPropName(breakpoint, 'cols')] = strNum()
42+
return props
43+
}, create(null))
44+
45+
// Cache the row-cols prop names
46+
rowColsPropList = keys(rowColsProps)
47+
48+
// Return the generated props
49+
return {
50+
tag: {
51+
type: String,
52+
default: 'div'
53+
},
54+
noGutters: {
55+
type: Boolean,
56+
default: false
57+
},
58+
alignV: {
59+
type: String,
60+
default: null,
61+
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['baseline', 'stretch']), str)
62+
},
63+
alignH: {
64+
type: String,
65+
default: null,
66+
validator: str => arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around']), str)
67+
},
68+
alignContent: {
69+
type: String,
70+
default: null,
71+
validator: str =>
72+
arrayIncludes(COMMON_ALIGNMENT.concat(['between', 'around', 'stretch']), str)
73+
},
74+
...rowColsProps
3075
}
3176
}
3277

78+
// We do not use `Vue.extend()` here as that would evaluate the props
79+
// immediately, which we do not want to happen
3380
// @vue/component
34-
export const BRow = /*#__PURE__*/ Vue.extend({
81+
export const BRow = {
3582
name: 'BRow',
3683
functional: true,
37-
props,
84+
get props() {
85+
// Allow props to be lazy evaled on first access and
86+
// then they become a non-getter afterwards
87+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get#Smart_self-overwriting_lazy_getters
88+
delete this.props
89+
this.props = generateProps()
90+
return this.props
91+
},
3892
render(h, { props, data, children }) {
39-
return h(
40-
props.tag,
41-
mergeData(data, {
42-
staticClass: 'row',
43-
class: {
44-
'no-gutters': props.noGutters,
45-
[`align-items-${props.alignV}`]: props.alignV,
46-
[`justify-content-${props.alignH}`]: props.alignH,
47-
[`align-content-${props.alignContent}`]: props.alignContent
48-
}
49-
}),
50-
children
51-
)
93+
const classList = []
94+
// Loop through row-cols breakpoint props and generate the classes
95+
rowColsPropList.forEach(prop => {
96+
const c = computeRowColsClass(computeRowColsBreakpoint(prop), props[prop])
97+
// If a class is returned, push it onto the array
98+
if (c) {
99+
classList.push(c)
100+
}
101+
})
102+
classList.push({
103+
'no-gutters': props.noGutters,
104+
[`align-items-${props.alignV}`]: props.alignV,
105+
[`justify-content-${props.alignH}`]: props.alignH,
106+
[`align-content-${props.alignContent}`]: props.alignContent
107+
})
108+
return h(props.tag, mergeData(data, { staticClass: 'row', class: classList }), children)
52109
}
53-
})
110+
}

0 commit comments

Comments
 (0)