diff --git a/README.md b/README.md index cad5385..07a6e73 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,18 @@ This is a Vue port for [react-content-loader](https://github.com/danilowoz/react ## Install -Note: For Vue 2, use `vue-content-loader@^0.2` instead. +⚠️ For Vue 2, use `vue-content-loader@^0.2` instead. + +With npm: + +```bash +npm i vue-content-loader +``` + +Or with yarn: ```bash yarn add vue-content-loader -# For Vue 2 -# yarn add vue-content-loader@^0.2 ``` CDN: [UNPKG](https://unpkg.com/vue-content-loader/) | [jsDelivr](https://cdn.jsdelivr.net/npm/vue-content-loader/) (available as `window.contentLoaders`) @@ -66,7 +72,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 +90,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/bili.config.js b/bili.config.js index fe810a8..3269627 100644 --- a/bili.config.js +++ b/bili.config.js @@ -1,6 +1,7 @@ /** @type {import('bili').Config} */ module.exports = { externals: ['vue'], + input: 'src/index.ts', output: { format: ['cjs', 'es', 'umd', 'umd-min'], fileName: 'vue-content-loader.[format][min][ext]', diff --git a/package.json b/package.json index 98b4951..533ec04 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "files": [ "dist" ], + "types": "dist/index.d.ts", "sideEffects": false, "scripts": { "prepublishOnly": "npm run build", @@ -39,6 +40,8 @@ "bili": "^5.0.5", "jest": "^26.6.3", "poi": "12.10.3", + "rollup-plugin-typescript2": "^0.29.0", + "typescript": "^4.1.3", "vue": "^3.0.5", "vue-loader": "^16.1.2", "vue-router": "^4.0.3" diff --git a/src/BulletListLoader.js b/src/BulletListLoader.tsx similarity index 81% rename from src/BulletListLoader.js rename to src/BulletListLoader.tsx index 6474f88..7bf10dd 100644 --- a/src/BulletListLoader.js +++ b/src/BulletListLoader.tsx @@ -1,7 +1,8 @@ +import { defineComponent } from 'vue' import ContentLoader from './ContentLoader' -const BulletListLoader = (props, { attrs }) => { - return ( +const BulletListLoader = defineComponent((props, { attrs }) => { + return () => ( @@ -13,6 +14,6 @@ const BulletListLoader = (props, { attrs }) => { ) -} +}) export default BulletListLoader diff --git a/src/CodeLoader.js b/src/CodeLoader.tsx similarity index 86% rename from src/CodeLoader.js rename to src/CodeLoader.tsx index 6baae3a..164ddf8 100644 --- a/src/CodeLoader.js +++ b/src/CodeLoader.tsx @@ -1,7 +1,8 @@ +import { defineComponent } from 'vue' import ContentLoader from './ContentLoader' -const CodeLoader = (props, { attrs }) => { - return ( +const CodeLoader = defineComponent((props, { attrs }) => { + return () => ( @@ -17,6 +18,6 @@ const CodeLoader = (props, { attrs }) => { ) -} +}) export default CodeLoader 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/ContentLoader.js b/src/ContentLoader.tsx similarity index 85% rename from src/ContentLoader.js rename to src/ContentLoader.tsx index 9c01de1..e594922 100644 --- a/src/ContentLoader.js +++ b/src/ContentLoader.tsx @@ -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(https://github.com/$%7Bthis.baseUrl%7D#${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/FacebookLoader.js b/src/FacebookLoader.tsx similarity index 80% rename from src/FacebookLoader.js rename to src/FacebookLoader.tsx index dae0f7d..39a5885 100644 --- a/src/FacebookLoader.js +++ b/src/FacebookLoader.tsx @@ -1,7 +1,8 @@ +import { defineComponent } from 'vue' import ContentLoader from './ContentLoader' -const FacebookLoader = (props, { attrs }) => { - return ( +const FacebookLoader = defineComponent((props, { attrs }) => { + return () => ( @@ -11,6 +12,6 @@ const FacebookLoader = (props, { attrs }) => { ) -} +}) export default FacebookLoader diff --git a/src/InstagramLoader.js b/src/InstagramLoader.tsx similarity index 66% rename from src/InstagramLoader.js rename to src/InstagramLoader.tsx index c0c36f8..6ccd206 100644 --- a/src/InstagramLoader.js +++ b/src/InstagramLoader.tsx @@ -1,8 +1,9 @@ +import { defineComponent } from 'vue' import ContentLoader from './ContentLoader' -const InstagramLoader = (props, { attrs }) => { - return ( - +const InstagramLoader = defineComponent((props, { attrs }) => { + return () => ( + @@ -10,6 +11,6 @@ const InstagramLoader = (props, { attrs }) => { ) -} +}) export default InstagramLoader diff --git a/src/ListLoader.js b/src/ListLoader.tsx similarity index 81% rename from src/ListLoader.js rename to src/ListLoader.tsx index 6f98217..2f349ef 100644 --- a/src/ListLoader.js +++ b/src/ListLoader.tsx @@ -1,7 +1,8 @@ +import { defineComponent } from 'vue' import ContentLoader from './ContentLoader' -const ListLoader = (props, { attrs }) => { - return ( +const ListLoader = defineComponent((props, { attrs }) => { + return () => ( @@ -11,6 +12,6 @@ const ListLoader = (props, { attrs }) => { ) -} +}) export default ListLoader diff --git a/src/index.js b/src/index.ts similarity index 100% rename from src/index.js rename to src/index.ts diff --git a/src/uid.js b/src/uid.ts similarity index 100% rename from src/uid.js rename to src/uid.ts diff --git a/stories/Storybook.vue b/stories/Storybook.vue index a1a8f78..602a7f2 100644 --- a/stories/Storybook.vue +++ b/stories/Storybook.vue @@ -53,12 +53,7 @@ export default { }, currentComponent() { - if (this.current) { - const { story } = this.current - return typeof story.component === 'function' - ? story.component() - : story.component - } + return this.current?.story.component; }, currentTitle() { 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 ( + + + + + + + + + ) } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5776620 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["node_modules", "dist"], + "compilerOptions": { + "rootDir": "src", + "target": "es5", + "module": "esnext", + "jsx": "preserve", + "moduleResolution": "node", + "declaration": true, + "sourceMap": true, + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/yarn.lock b/yarn.lock index a8b6289..0c53f52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2370,9 +2370,9 @@ integrity sha512-gYsNoGkWejBxNO6SNRjOh/xKeZ0H0V+TFzaPzODfBjkAIb0aQgBuixC1brandC/CDJy1wYPwSoYrXpvul7m6yw== "@vue/test-utils@^2.0.0-beta.14": - version "2.0.0-beta.14" - resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-beta.14.tgz#9300d48789b334b52c5514efdc4648f704a7e94b" - integrity sha512-lYPoUcp6zd+CBOvrhJYuvWBpyNrSJqiVwRsT73aTpx0c5p9nAz1coKU9ou+T5xdvHf++Ngqaif/2VjD0phv0yQ== + version "2.0.0-rc.0" + resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-rc.0.tgz#c9bc0f898f386697680fe0991a558876fdd178ad" + integrity sha512-sutAkUuE0gxlpzMO4V5SzzEujxdrTDr8YAK2KhhhmLKoNGX59UURXfW+DTLgaygB1n+i6nhs/WD+2A4wtjncSA== "@webassemblyjs/ast@1.8.5": version "1.8.5" @@ -5080,6 +5080,15 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-extra@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc" @@ -6634,6 +6643,13 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -8910,6 +8926,13 @@ resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" +resolve@1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + resolve@^1.10.0, resolve@^1.8.1: version "1.11.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" @@ -9034,6 +9057,17 @@ rollup-plugin-terser@^6.1.0: serialize-javascript "^3.0.0" terser "^4.7.0" +rollup-plugin-typescript2@^0.29.0: + version "0.29.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.29.0.tgz#b7ad83f5241dbc5bdf1e98d9c3fca005ffe39e1a" + integrity sha512-YytahBSZCIjn/elFugEGQR5qTsVhxhUwGZIsA9TmrSsC88qroGo65O5HZP/TTArH2dm0vUmYWhKchhwi2wL9bw== + dependencies: + "@rollup/pluginutils" "^3.1.0" + find-cache-dir "^3.3.1" + fs-extra "8.1.0" + resolve "1.17.0" + tslib "2.0.1" + rollup-pluginutils@^2.8.2: version "2.8.2" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" @@ -10028,6 +10062,11 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +tslib@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" + integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== + tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" @@ -10099,6 +10138,11 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +typescript@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== + uglify-js@3.4.x: version "3.4.10" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" @@ -10165,6 +10209,11 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + universalify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"