1
|
1
|
import './VGrid.sass'
|
2
|
|
|
3
|
1
|
import Vue, { VNode, PropOptions } from 'vue'
|
4
|
1
|
import mergeData from '../../util/mergeData'
|
5
|
1
|
import { upperFirst } from '../../util/helpers'
|
6
|
|
|
7
|
|
// no xs
|
8
|
1
|
const breakpoints = ['sm', 'md', 'lg', 'xl']
|
9
|
|
|
10
|
1
|
const breakpointProps = (() => {
|
11
|
1
|
return breakpoints.reduce((props, val) => {
|
12
|
1
|
props[val] = {
|
13
|
|
type: [Boolean, String, Number],
|
14
|
|
default: false,
|
15
|
|
}
|
16
|
1
|
return props
|
17
|
|
}, {} as Dictionary<PropOptions>)
|
18
|
|
})()
|
19
|
|
|
20
|
1
|
const offsetProps = (() => {
|
21
|
1
|
return breakpoints.reduce((props, val) => {
|
22
|
1
|
props['offset' + upperFirst(val)] = {
|
23
|
|
type: [String, Number],
|
24
|
|
default: null,
|
25
|
|
}
|
26
|
1
|
return props
|
27
|
|
}, {} as Dictionary<PropOptions>)
|
28
|
|
})()
|
29
|
|
|
30
|
1
|
const orderProps = (() => {
|
31
|
1
|
return breakpoints.reduce((props, val) => {
|
32
|
1
|
props['order' + upperFirst(val)] = {
|
33
|
|
type: [String, Number],
|
34
|
|
default: null,
|
35
|
|
}
|
36
|
1
|
return props
|
37
|
|
}, {} as Dictionary<PropOptions>)
|
38
|
|
})()
|
39
|
|
|
40
|
1
|
const propMap = {
|
41
|
|
col: Object.keys(breakpointProps),
|
42
|
|
offset: Object.keys(offsetProps),
|
43
|
|
order: Object.keys(orderProps),
|
44
|
|
}
|
45
|
|
|
46
|
1
|
function breakpointClass (type: keyof typeof propMap, prop: string, val: boolean | string | number) {
|
47
|
1
|
let className = type
|
48
|
1
|
if (val == null || val === false) {
|
49
|
1
|
return undefined
|
50
|
|
}
|
51
|
1
|
if (prop) {
|
52
|
1
|
const breakpoint = prop.replace(type, '')
|
53
|
1
|
className += `-${breakpoint}`
|
54
|
|
}
|
55
|
|
// Handling the boolean style prop when accepting [Boolean, String, Number]
|
56
|
|
// means Vue will not convert <v-col sm></v-col> to sm: true for us.
|
57
|
|
// Since the default is false, an empty string indicates the prop's presence.
|
58
|
1
|
if (type === 'col' && (val === '' || val === true)) {
|
59
|
|
// .col-md
|
60
|
1
|
return className.toLowerCase()
|
61
|
|
}
|
62
|
|
// .order-md-6
|
63
|
1
|
className += `-${val}`
|
64
|
1
|
return className.toLowerCase()
|
65
|
|
}
|
66
|
|
|
67
|
1
|
const cache = new Map<string, any[]>()
|
68
|
|
|
69
|
1
|
export default Vue.extend({
|
70
|
|
name: 'v-col',
|
71
|
|
functional: true,
|
72
|
|
props: {
|
73
|
|
cols: {
|
74
|
|
type: [Boolean, String, Number],
|
75
|
|
default: false,
|
76
|
|
},
|
77
|
|
...breakpointProps,
|
78
|
|
offset: {
|
79
|
|
type: [String, Number],
|
80
|
|
default: null,
|
81
|
|
},
|
82
|
|
...offsetProps,
|
83
|
|
order: {
|
84
|
|
type: [String, Number],
|
85
|
|
default: null,
|
86
|
|
},
|
87
|
|
...orderProps,
|
88
|
|
alignSelf: {
|
89
|
|
type: String,
|
90
|
|
default: null,
|
91
|
1
|
validator: (str: any) => ['auto', 'start', 'end', 'center', 'baseline', 'stretch'].includes(str),
|
92
|
|
},
|
93
|
|
tag: {
|
94
|
|
type: String,
|
95
|
|
default: 'div',
|
96
|
|
},
|
97
|
|
},
|
98
|
1
|
render (h, { props, data, children, parent }): VNode {
|
99
|
|
// Super-fast memoization based on props, 5x faster than JSON.stringify
|
100
|
1
|
let cacheKey = ''
|
101
|
1
|
for (const prop in props) {
|
102
|
1
|
cacheKey += String((props as any)[prop])
|
103
|
|
}
|
104
|
1
|
let classList = cache.get(cacheKey)
|
105
|
|
|
106
|
1
|
if (!classList) {
|
107
|
1
|
classList = []
|
108
|
|
// Loop through `col`, `offset`, `order` breakpoint props
|
109
|
|
let type: keyof typeof propMap
|
110
|
1
|
for (type in propMap) {
|
111
|
1
|
propMap[type].forEach(prop => {
|
112
|
1
|
const value: string | number | boolean = (props as any)[prop]
|
113
|
1
|
const className = breakpointClass(type, prop, value)
|
114
|
1
|
if (className) classList!.push(className)
|
115
|
|
})
|
116
|
|
}
|
117
|
|
|
118
|
1
|
const hasColClasses = classList.some(className => className.startsWith('col-'))
|
119
|
|
|
120
|
1
|
classList.push({
|
121
|
|
// Default to .col if no other col-{bp}-* classes generated nor `cols` specified.
|
122
|
1
|
col: !hasColClasses || !props.cols,
|
123
|
|
[`col-${props.cols}`]: props.cols,
|
124
|
|
[`offset-${props.offset}`]: props.offset,
|
125
|
|
[`order-${props.order}`]: props.order,
|
126
|
|
[`align-self-${props.alignSelf}`]: props.alignSelf,
|
127
|
|
})
|
128
|
|
|
129
|
1
|
cache.set(cacheKey, classList)
|
130
|
|
}
|
131
|
|
|
132
|
1
|
return h(props.tag, mergeData(data, { class: classList }), children)
|
133
|
|
},
|
134
|
|
})
|