Skip to content

Commit 1a6fe75

Browse files
committed
fix: comply to width & height attributes
fix #46 BREAKING CHANGE: `width` and `height` attributes are now applied as-is to the `svg` element, if you want the previous, responsive behavior, use only the new `viewBox` prop instead. Before: `width="200" height="100"` After: `viewBox="0 0 200 100"`
1 parent 547a83c commit 1a6fe75

File tree

5 files changed

+88
-41
lines changed

5 files changed

+88
-41
lines changed

README.md

+37-14
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import {
6666
`ContentLoader` is a meta loader while other loaders are just higher-order components of it. By default `ContentLoader` only displays a simple rectangle, here's how you can use it to create custom loaders:
6767

6868
```vue
69-
<ContentLoader>
69+
<ContentLoader viewBox="0 0 250 110">
7070
<rect x="0" y="0" rx="3" ry="3" width="250" height="10" />
7171
<rect x="20" y="20" rx="3" ry="3" width="220" height="10" />
7272
<rect x="20" y="40" rx="3" ry="3" width="170" height="10" />
@@ -84,19 +84,42 @@ You can also use the [online tool](http://danilowoz.com/create-vue-content-loade
8484

8585
### Props
8686

87-
| Prop | Type | Default | Description |
88-
| ------------------- | ------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
89-
| width | number | `400` | |
90-
| height | number | `130` | |
91-
| speed | number | `2` | |
92-
| preserveAspectRatio | string | `'xMidYMid meet'` | |
93-
| primaryColor | string | `'#f9f9f9'` | |
94-
| secondaryColor | string | `'#ecebeb'` | |
95-
| uniqueKey | string | `randomId()` | Unique ID, you need to make it consistent for SSR |
96-
| animate | boolean | `true` | |
97-
| baseUrl | string | empty string | Required if you're using `<base url="/" />` in your `<head />`. Defaults to an empty string. This prop is common used as: `<content-loader :base-url="$route.fullPath" />` which will fill the SVG attribute with the relative path. Related [#14](https://github.com/egoist/vue-content-loader/issues/14). |
98-
| primaryOpacity | number | `1` | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in Safari |
99-
| secondaryOpacity | number | `1` | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in Safari |
87+
| Prop | Type | Default | Description |
88+
| ------------------- | -------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
89+
| width | number, string | | SVG width in pixels without unit |
90+
| height | number, string | | SVG height in pixels without unit |
91+
| viewBox | string | `'0 0 ${width || 400} ${height || 130}'` | See [SVG viewBox](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox) attribute |
92+
| preserveAspectRatio | string | `'xMidYMid meet'` | See [SVG preserveAspectRatio](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio) attribute |
93+
| speed | number | `2` | Animation speed |
94+
| primaryColor | string | `'#f9f9f9'` | Background color |
95+
| secondaryColor | string | `'#ecebeb'` | Highlight color |
96+
| uniqueKey | string | `randomId()` | Unique ID, you need to make it consistent for SSR |
97+
| animate | boolean | `true` | |
98+
| baseUrl | string | empty string | Required if you're using `<base url="/" />` in your `<head />`. Defaults to an empty string. This prop is common used as: `<content-loader :base-url="$route.fullPath" />` which will fill the SVG attribute with the relative path. Related [#14](https://github.com/egoist/vue-content-loader/issues/14). |
99+
| primaryOpacity | number | `1` | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in Safari |
100+
| secondaryOpacity | number | `1` | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in Safari |
101+
102+
## Examples
103+
104+
### Responsiveness
105+
106+
To create a responsive loader that will follow its parent container width, use only the `viewBox` attribute to set the ratio:
107+
108+
```vue
109+
<ContentLoader viewBox="0 0 300 200">
110+
<!-- ... -->
111+
</ContentLoader>
112+
```
113+
114+
To create a loader with fixed dimensions, use `width` and `height` attributes:
115+
116+
```vue
117+
<ContentLoader width="300" height="200">
118+
<!-- ... -->
119+
</ContentLoader>
120+
```
121+
122+
Note: the exact behavior might be different depending on the CSS you apply to SVG elements.
100123

101124
## Credits
102125

src/ContentLoader.js

+19-19
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@ export default defineComponent({
66

77
props: {
88
width: {
9-
type: [Number, String],
10-
default: 400
9+
type: [Number, String]
1110
},
1211
height: {
13-
type: [Number, String],
14-
default: 130
12+
type: [Number, String]
1513
},
16-
speed: {
17-
type: Number,
18-
default: 2
14+
viewBox: {
15+
type: String
1916
},
2017
preserveAspectRatio: {
2118
type: String,
2219
default: 'xMidYMid meet'
2320
},
21+
speed: {
22+
type: Number,
23+
default: 2
24+
},
2425
baseUrl: {
2526
type: String,
2627
default: ''
@@ -53,17 +54,23 @@ export default defineComponent({
5354
setup(props) {
5455
const idClip = props.uniqueKey ? `${props.uniqueKey}-idClip` : uid()
5556
const idGradient = props.uniqueKey ? `${props.uniqueKey}-idGradient` : uid()
57+
const width = props.width ?? 400
58+
const height = props.height ?? 130
59+
const computedViewBox = props.viewBox ?? `0 0 ${width} ${height}`
5660

5761
return {
5862
idClip,
59-
idGradient
63+
idGradient,
64+
computedViewBox
6065
}
6166
},
6267

6368
render() {
6469
return (
6570
<svg
66-
viewBox={`0 0 ${this.width} ${this.height}`}
71+
width={this.width}
72+
height={this.height}
73+
viewBox={this.computedViewBox}
6774
version="1.1"
6875
preserveAspectRatio={this.preserveAspectRatio}
6976
>
@@ -72,23 +79,16 @@ export default defineComponent({
7279
clip-path={`url(${this.baseUrl}#${this.idClip})`}
7380
x="0"
7481
y="0"
75-
width={this.width}
76-
height={this.height}
82+
width="100%"
83+
height="100%"
7784
/>
7885

7986
<defs>
8087
<clipPath id={this.idClip}>
8188
{this.$slots.default ? (
8289
this.$slots.default()
8390
) : (
84-
<rect
85-
x="0"
86-
y="0"
87-
rx="5"
88-
ry="5"
89-
width={this.width}
90-
height={this.height}
91-
/>
91+
<rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" />
9292
)}
9393
</clipPath>
9494

src/ContentLoader.spec.js

+11-6
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ describe('ContentLoader', () => {
55
it('has default values for props', () => {
66
const wrapper = mount(ContentLoader)
77

8-
expect(wrapper.vm.width).toBe(400)
9-
expect(wrapper.vm.height).toBe(130)
8+
expect(wrapper.vm.width).toBe(undefined)
9+
expect(wrapper.vm.height).toBe(undefined)
1010
expect(wrapper.vm.speed).toBe(2)
1111
expect(wrapper.vm.preserveAspectRatio).toBe('xMidYMid meet')
1212
expect(wrapper.vm.baseUrl).toBe('')
1313
expect(wrapper.vm.primaryColor).toBe('#f9f9f9')
1414
expect(wrapper.vm.secondaryColor).toBe('#ecebeb')
1515
expect(wrapper.vm.primaryOpacity).toBe(1)
1616
expect(wrapper.vm.secondaryOpacity).toBe(1)
17+
expect(wrapper.vm.uniqueKey).toBe(undefined)
1718
expect(wrapper.vm.animate).toBe(true)
1819
})
1920

@@ -27,6 +28,8 @@ describe('ContentLoader', () => {
2728
})
2829

2930
expect(wrapper.find('svg').attributes()).toEqual({
31+
width: '300',
32+
height: '200',
3033
preserveAspectRatio: 'xMaxYMid slice',
3134
version: '1.1',
3235
viewBox: '0 0 300 200'
@@ -43,6 +46,8 @@ describe('ContentLoader', () => {
4346
})
4447

4548
expect(wrapper.find('svg').attributes()).toEqual({
49+
width: '300',
50+
height: '200',
4651
preserveAspectRatio: 'xMaxYMid slice',
4752
version: '1.1',
4853
viewBox: '0 0 300 200'
@@ -57,8 +62,8 @@ describe('ContentLoader', () => {
5762
'clip-path': `url(#${wrapper.vm.idClip})`,
5863
x: '0',
5964
y: '0',
60-
width: '400',
61-
height: '130'
65+
width: '100%',
66+
height: '100%'
6267
})
6368
})
6469

@@ -143,8 +148,8 @@ describe('ContentLoader', () => {
143148
y: '0',
144149
rx: '5',
145150
ry: '5',
146-
width: '400',
147-
height: '130'
151+
width: '100%',
152+
height: '100%'
148153
})
149154
})
150155

src/InstagramLoader.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import ContentLoader from './ContentLoader'
22

33
const InstagramLoader = (props, { attrs }) => {
44
return (
5-
<ContentLoader {...attrs} height={480}>
5+
<ContentLoader {...attrs} viewBox="0 0 400 480">
66
<circle cx="30" cy="30" r="30" />
77

88
<rect x="75" y="13" rx="4" ry="4" width="100" height="13" />

stories/index.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,26 @@ section
135135
render() {
136136
return (
137137
<Container>
138-
<ContentLoader width={400} height={150} />
138+
<ContentLoader width={200} height={100} />
139+
</Container>
140+
)
141+
}
142+
}
143+
})
144+
.addStory({
145+
title: 'custom viewBox',
146+
component: {
147+
render() {
148+
return (
149+
<Container>
150+
<ContentLoader viewBox="0 0 250 110">
151+
<rect x="0" y="0" rx="3" ry="3" width="250" height="10" />
152+
<rect x="20" y="20" rx="3" ry="3" width="220" height="10" />
153+
<rect x="20" y="40" rx="3" ry="3" width="170" height="10" />
154+
<rect x="0" y="60" rx="3" ry="3" width="250" height="10" />
155+
<rect x="20" y="80" rx="3" ry="3" width="200" height="10" />
156+
<rect x="20" y="100" rx="3" ry="3" width="80" height="10" />
157+
</ContentLoader>
139158
</Container>
140159
)
141160
}

0 commit comments

Comments
 (0)