From 1a6fe754d91ee9dfff137622ad4ba16a7d4e8797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benoi=CC=82t=20Burgener?= Date: Wed, 27 Jan 2021 09:49:52 +0100 Subject: [PATCH] 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"` --- README.md | 51 ++++++++++++++++++++++++++++----------- src/ContentLoader.js | 38 ++++++++++++++--------------- src/ContentLoader.spec.js | 17 ++++++++----- src/InstagramLoader.js | 2 +- stories/index.js | 21 +++++++++++++++- 5 files changed, 88 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index cad5385..9f03e7b 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ import { `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: ```vue - + @@ -84,19 +84,42 @@ You can also use the [online tool](http://danilowoz.com/create-vue-content-loade ### Props -| Prop | Type | Default | Description | -| ------------------- | ------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| width | number | `400` | | -| height | number | `130` | | -| speed | number | `2` | | -| preserveAspectRatio | string | `'xMidYMid meet'` | | -| primaryColor | string | `'#f9f9f9'` | | -| secondaryColor | string | `'#ecebeb'` | | -| uniqueKey | string | `randomId()` | Unique ID, you need to make it consistent for SSR | -| animate | boolean | `true` | | -| baseUrl | string | empty string | Required if you're using `` in your ``. Defaults to an empty string. This prop is common used as: `` which will fill the SVG attribute with the relative path. Related [#14](https://github.com/egoist/vue-content-loader/issues/14). | -| primaryOpacity | number | `1` | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in Safari | -| secondaryOpacity | number | `1` | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in Safari | +| Prop | Type | Default | Description | +| ------------------- | -------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| width | number, string | | SVG width in pixels without unit | +| height | number, string | | SVG height in pixels without unit | +| viewBox | string | `'0 0 ${width || 400} ${height || 130}'` | See [SVG viewBox](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox) attribute | +| preserveAspectRatio | string | `'xMidYMid meet'` | See [SVG preserveAspectRatio](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio) attribute | +| speed | number | `2` | Animation speed | +| primaryColor | string | `'#f9f9f9'` | Background color | +| secondaryColor | string | `'#ecebeb'` | Highlight color | +| uniqueKey | string | `randomId()` | Unique ID, you need to make it consistent for SSR | +| animate | boolean | `true` | | +| baseUrl | string | empty string | Required if you're using `` in your ``. Defaults to an empty string. This prop is common used as: `` which will fill the SVG attribute with the relative path. Related [#14](https://github.com/egoist/vue-content-loader/issues/14). | +| primaryOpacity | number | `1` | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in Safari | +| secondaryOpacity | number | `1` | Background opacity (0 = transparent, 1 = opaque) used to solve an issue in Safari | + +## Examples + +### Responsiveness + +To create a responsive loader that will follow its parent container width, use only the `viewBox` attribute to set the ratio: + +```vue + + + +``` + +To create a loader with fixed dimensions, use `width` and `height` attributes: + +```vue + + + +``` + +Note: the exact behavior might be different depending on the CSS you apply to SVG elements. ## Credits diff --git a/src/ContentLoader.js b/src/ContentLoader.js index 9c01de1..e594922 100644 --- a/src/ContentLoader.js +++ b/src/ContentLoader.js @@ -6,21 +6,22 @@ export default defineComponent({ props: { width: { - type: [Number, String], - default: 400 + type: [Number, String] }, height: { - type: [Number, String], - default: 130 + type: [Number, String] }, - speed: { - type: Number, - default: 2 + viewBox: { + type: String }, preserveAspectRatio: { type: String, default: 'xMidYMid meet' }, + speed: { + type: Number, + default: 2 + }, baseUrl: { type: String, default: '' @@ -53,17 +54,23 @@ export default defineComponent({ setup(props) { const idClip = props.uniqueKey ? `${props.uniqueKey}-idClip` : uid() const idGradient = props.uniqueKey ? `${props.uniqueKey}-idGradient` : uid() + const width = props.width ?? 400 + const height = props.height ?? 130 + const computedViewBox = props.viewBox ?? `0 0 ${width} ${height}` return { idClip, - idGradient + idGradient, + computedViewBox } }, render() { return ( @@ -72,8 +79,8 @@ export default defineComponent({ clip-path={`url(${this.baseUrl}#${this.idClip})`} x="0" y="0" - width={this.width} - height={this.height} + width="100%" + height="100%" /> @@ -81,14 +88,7 @@ export default defineComponent({ {this.$slots.default ? ( this.$slots.default() ) : ( - + )} diff --git a/src/ContentLoader.spec.js b/src/ContentLoader.spec.js index d8ea7db..bdb63c7 100644 --- a/src/ContentLoader.spec.js +++ b/src/ContentLoader.spec.js @@ -5,8 +5,8 @@ describe('ContentLoader', () => { it('has default values for props', () => { const wrapper = mount(ContentLoader) - expect(wrapper.vm.width).toBe(400) - expect(wrapper.vm.height).toBe(130) + expect(wrapper.vm.width).toBe(undefined) + expect(wrapper.vm.height).toBe(undefined) expect(wrapper.vm.speed).toBe(2) expect(wrapper.vm.preserveAspectRatio).toBe('xMidYMid meet') expect(wrapper.vm.baseUrl).toBe('') @@ -14,6 +14,7 @@ describe('ContentLoader', () => { expect(wrapper.vm.secondaryColor).toBe('#ecebeb') expect(wrapper.vm.primaryOpacity).toBe(1) expect(wrapper.vm.secondaryOpacity).toBe(1) + expect(wrapper.vm.uniqueKey).toBe(undefined) expect(wrapper.vm.animate).toBe(true) }) @@ -27,6 +28,8 @@ describe('ContentLoader', () => { }) expect(wrapper.find('svg').attributes()).toEqual({ + width: '300', + height: '200', preserveAspectRatio: 'xMaxYMid slice', version: '1.1', viewBox: '0 0 300 200' @@ -43,6 +46,8 @@ describe('ContentLoader', () => { }) expect(wrapper.find('svg').attributes()).toEqual({ + width: '300', + height: '200', preserveAspectRatio: 'xMaxYMid slice', version: '1.1', viewBox: '0 0 300 200' @@ -57,8 +62,8 @@ describe('ContentLoader', () => { 'clip-path': `url(#${wrapper.vm.idClip})`, x: '0', y: '0', - width: '400', - height: '130' + width: '100%', + height: '100%' }) }) @@ -143,8 +148,8 @@ describe('ContentLoader', () => { y: '0', rx: '5', ry: '5', - width: '400', - height: '130' + width: '100%', + height: '100%' }) }) diff --git a/src/InstagramLoader.js b/src/InstagramLoader.js index c0c36f8..f6c539f 100644 --- a/src/InstagramLoader.js +++ b/src/InstagramLoader.js @@ -2,7 +2,7 @@ import ContentLoader from './ContentLoader' const InstagramLoader = (props, { attrs }) => { return ( - + diff --git a/stories/index.js b/stories/index.js index 2eb367f..ef4d58b 100644 --- a/stories/index.js +++ b/stories/index.js @@ -135,7 +135,26 @@ section render() { return ( - + + + ) + } + } + }) + .addStory({ + title: 'custom viewBox', + component: { + render() { + return ( + + + + + + + + + ) }