Skip to content

Commit 74c4d95

Browse files
committed
feat: Added BootstrapVue image components.
1 parent f886707 commit 74c4d95

File tree

4 files changed

+338
-0
lines changed

4 files changed

+338
-0
lines changed

src/components/Image/CImage.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { mergeData } from 'vue-functional-data-merge'
2+
3+
// Blank image with fill template
4+
const BLANK_TEMPLATE = '<svg width="%{w}" height="%{h}" ' +
5+
'xmlns="http://www.w3.org/2000/svg" ' +
6+
'viewBox="0 0 %{w} %{h}" preserveAspectRatio="none">' +
7+
'<rect width="100%" height="100%" style="fill:%{f};"></rect>' +
8+
'</svg>'
9+
10+
function makeBlankImgSrc (width, height, color) {
11+
const src = encodeURIComponent(
12+
BLANK_TEMPLATE
13+
.replace('%{w}', String(width))
14+
.replace('%{h}', String(height))
15+
.replace('%{f}', color)
16+
)
17+
return `data:image/svg+xml;charset=UTF-8,${src}`
18+
}
19+
20+
export const props = {
21+
src: {
22+
type: String,
23+
default: null
24+
},
25+
alt: {
26+
type: String,
27+
default: null
28+
},
29+
width: {
30+
type: [Number, String],
31+
default: null
32+
},
33+
height: {
34+
type: [Number, String],
35+
default: null
36+
},
37+
block: {
38+
type: Boolean,
39+
default: false
40+
},
41+
fluid: {
42+
type: Boolean,
43+
default: false
44+
},
45+
fluidGrow: {
46+
// Gives fluid images class `w-100` to make them grow to fit container
47+
type: Boolean,
48+
default: false
49+
},
50+
rounded: {
51+
// rounded can be:
52+
// false: no rounding of corners
53+
// true: slightly rounded corners
54+
// 'top': top corners rounded
55+
// 'right': right corners rounded
56+
// 'bottom': bottom corners rounded
57+
// 'left': left corners rounded
58+
// 'circle': circle/oval
59+
// '0': force rounding off
60+
type: [Boolean, String],
61+
default: false
62+
},
63+
thumbnail: {
64+
type: Boolean,
65+
default: false
66+
},
67+
left: {
68+
type: Boolean,
69+
default: false
70+
},
71+
right: {
72+
type: Boolean,
73+
default: false
74+
},
75+
center: {
76+
type: Boolean,
77+
default: false
78+
},
79+
blank: {
80+
type: Boolean,
81+
default: false
82+
},
83+
blankColor: {
84+
type: String,
85+
default: 'transparent'
86+
}
87+
}
88+
89+
// @vue/component
90+
export default {
91+
functional: true,
92+
name: 'CImage',
93+
props,
94+
render (h, { props, data }) {
95+
let src = props.src
96+
let width = parseInt(props.width, 10) ? parseInt(props.width, 10) : null
97+
let height = parseInt(props.height, 10) ? parseInt(props.height, 10) : null
98+
let align = null
99+
let block = props.block
100+
if (props.blank) {
101+
if (!height && Boolean(width)) {
102+
height = width
103+
} else if (!width && Boolean(height)) {
104+
width = height
105+
}
106+
if (!width && !height) {
107+
width = 1
108+
height = 1
109+
}
110+
// Make a blank SVG image
111+
src = makeBlankImgSrc(width, height, props.blankColor || 'transparent')
112+
}
113+
if (props.left) {
114+
align = 'float-left'
115+
} else if (props.right) {
116+
align = 'float-right'
117+
} else if (props.center) {
118+
align = 'mx-auto'
119+
block = true
120+
}
121+
return h(
122+
'img',
123+
mergeData(data, {
124+
attrs: {
125+
'src': src,
126+
'alt': props.alt,
127+
'width': width ? String(width) : null,
128+
'height': height ? String(height) : null
129+
},
130+
class: {
131+
'img-thumbnail': props.thumbnail,
132+
'img-fluid': props.fluid || props.fluidGrow,
133+
'w-100': props.fluidGrow,
134+
'rounded': props.rounded === '' || props.rounded === true,
135+
[`rounded-${props.rounded}`]: typeof props.rounded === 'string' && props.rounded !== '',
136+
[align]: Boolean(align),
137+
'd-block': block
138+
}
139+
})
140+
)
141+
}
142+
}

src/components/Image/CImageLazy.js

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import CImage from './CImage'
2+
import { isVisible, getBCR, eventOn, eventOff } from '../../utils/dom'
3+
const THROTTLE = 100
4+
5+
// @vue/component
6+
export default {
7+
components: { CImage },
8+
name: 'CImageLazy',
9+
props: {
10+
src: {
11+
type: String,
12+
default: null,
13+
required: true
14+
},
15+
alt: {
16+
type: String,
17+
default: null
18+
},
19+
width: {
20+
type: [Number, String],
21+
default: null
22+
},
23+
height: {
24+
type: [Number, String],
25+
default: null
26+
},
27+
blankSrc: {
28+
// If null, a blank image is generated
29+
type: String,
30+
default: null
31+
},
32+
blankColor: {
33+
type: String,
34+
default: 'transparent'
35+
},
36+
blankWidth: {
37+
type: [Number, String],
38+
default: null
39+
},
40+
blankHeight: {
41+
type: [Number, String],
42+
default: null
43+
},
44+
fluid: {
45+
type: Boolean,
46+
default: false
47+
},
48+
fluidGrow: {
49+
type: Boolean,
50+
default: false
51+
},
52+
block: {
53+
type: Boolean,
54+
default: false
55+
},
56+
thumbnail: {
57+
type: Boolean,
58+
default: false
59+
},
60+
rounded: {
61+
type: [Boolean, String],
62+
default: false
63+
},
64+
left: {
65+
type: Boolean,
66+
default: false
67+
},
68+
right: {
69+
type: Boolean,
70+
default: false
71+
},
72+
center: {
73+
type: Boolean,
74+
default: false
75+
},
76+
offset: {
77+
type: [Number, String],
78+
default: 360
79+
},
80+
throttle: {
81+
type: [Number, String],
82+
default: THROTTLE
83+
}
84+
},
85+
data () {
86+
return {
87+
isShown: false,
88+
scrollTimeout: null
89+
}
90+
},
91+
computed: {
92+
computedSrc () {
93+
return (!this.blankSrc || this.isShown) ? this.src : this.blankSrc
94+
},
95+
computedBlank () {
96+
return !((this.isShown || this.blankSrc))
97+
},
98+
computedWidth () {
99+
return this.isShown ? this.width : (this.blankWidth || this.width)
100+
},
101+
computedHeight () {
102+
return this.isShown ? this.height : (this.blankHeight || this.height)
103+
}
104+
},
105+
mounted () {
106+
this.setListeners(true)
107+
this.checkView()
108+
},
109+
activated () {
110+
this.setListeners(true)
111+
this.checkView()
112+
},
113+
deactivated () {
114+
this.setListeners(false)
115+
},
116+
methods: {
117+
setListeners (on) {
118+
clearTimeout(this.scrollTimer)
119+
this.scrollTimeout = null
120+
const root = window
121+
if (on) {
122+
eventOn(root, 'scroll', this.onScroll)
123+
eventOn(root, 'resize', this.onScroll)
124+
eventOn(root, 'orientationchange', this.onScroll)
125+
} else {
126+
eventOff(root, 'scroll', this.onScroll)
127+
eventOff(root, 'resize', this.onScroll)
128+
eventOff(root, 'orientationchange', this.onScroll)
129+
}
130+
},
131+
checkView () {
132+
// check bounding box + offset to see if we should show
133+
if (!isVisible(this.$el)) {
134+
// Element is hidden, so skip for now
135+
return
136+
}
137+
const offset = parseInt(this.offset, 10) || 0
138+
const docElement = document.documentElement
139+
const view = {
140+
l: 0 - offset,
141+
t: 0 - offset,
142+
b: docElement.clientHeight + offset,
143+
r: docElement.clientWidth + offset
144+
}
145+
const box = getBCR(this.$el)
146+
if (box.right >= view.l && box.bottom >= view.t && box.left <= view.r && box.top <= view.b) {
147+
// image is in view (or about to be in view)
148+
this.isShown = true
149+
this.setListeners(false)
150+
}
151+
},
152+
onScroll () {
153+
if (this.isShown) {
154+
this.setListeners(false)
155+
} else {
156+
clearTimeout(this.scrollTimeout)
157+
this.scrollTimeout = setTimeout(this.checkView, parseInt(this.throttle, 10) || THROTTLE)
158+
}
159+
}
160+
},
161+
render (h) {
162+
return h(
163+
'CImage',
164+
{
165+
props: {
166+
src: this.computedSrc,
167+
alt: this.alt,
168+
blank: this.computedBlank,
169+
blankColor: this.blankColor,
170+
width: this.computedWidth,
171+
height: this.computedHeight,
172+
fluid: this.fluid,
173+
fluidGrow: this.fluidGrow,
174+
block: this.block,
175+
thumbnail: this.thumbnail,
176+
rounded: this.rounded,
177+
left: this.left,
178+
right: this.right,
179+
center: this.center
180+
}
181+
}
182+
)
183+
},
184+
beforeDdestroy () {
185+
this.setListeners(false)
186+
}
187+
}

src/components/Image/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import CImage from './CImage'
2+
import CImageLazy from './CImageLazy'
3+
4+
5+
export {
6+
CImage,
7+
CImageLazy
8+
}

src/components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ export * from './Alert'
1515
export * from './Button'
1616
export * from './Card'
1717
export * from './Dropdown'
18+
export * from './Image'

0 commit comments

Comments
 (0)