├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .lintstagedrc
├── .prettierrc
├── .snyk
├── .travis.yml
├── LICENSE.md
├── README.md
├── babel.config.js
├── codecov.yml
├── example
├── .babelrc
├── .gitignore
├── babel.config.js
├── jest.config.js
├── package.json
├── src
│ ├── components
│ │ ├── code.js
│ │ ├── colorPicker.js
│ │ ├── form.js
│ │ ├── index.js
│ │ ├── layout.js
│ │ └── loaderItem.js
│ ├── main.js
│ └── styles
│ │ └── index.scss
└── yarn.lock
├── greenkeeper.json
├── jest.config.js
├── package.json
├── public
└── index.html
├── src
├── components
│ ├── __tests__
│ │ └── barLoader.spec.js
│ ├── barLoader.js
│ ├── beatLoader.js
│ ├── bounceLoader.js
│ ├── circleLoader.js
│ ├── climbingBoxLoader.js
│ ├── clipLoader.js
│ ├── dotLoader.js
│ ├── fadeLoader.js
│ ├── gridLoader.js
│ ├── hashLoader.js
│ ├── index.js
│ ├── moonLoader.js
│ ├── pacmanLoader.js
│ ├── propagateLoader.js
│ ├── pulseLoader.js
│ ├── ringLoader.js
│ ├── riseLoader.js
│ ├── rotateLoader.js
│ ├── scaleLoader.js
│ ├── skewLoader.js
│ ├── squareLoader.js
│ └── syncLoader.js
├── index.js
└── utils.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 | /test/unit/coverage/
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const { join } = require(`path`)
2 |
3 | const srcDir = join(__dirname, `src`)
4 |
5 | module.exports = {
6 | root: true,
7 |
8 | env: {
9 | es6: true,
10 | node: true,
11 | browser: true,
12 | jest: true
13 | },
14 |
15 | plugins: [
16 | `import`, // https://github.com/benmosher/eslint-plugin-import
17 | `jest`, // https://github.com/jest-community/eslint-plugin-jest
18 | `promise`, // https://github.com/xjamundx/eslint-plugin-promise
19 | `vue` // https://github.com/vuejs/eslint-plugin-vue
20 | ],
21 |
22 | extends: [
23 | `plugin:jest/recommended`,
24 | 'plugin:vue/essential',
25 | `plugin:vue/recommended`
26 | ],
27 |
28 | settings: {
29 | polyfills: [`fetch`, `Promise`]
30 | },
31 |
32 | parser: `vue-eslint-parser`,
33 |
34 | parserOptions: {
35 | parser: 'babel-eslint',
36 | ecmaVersion: 2017,
37 | sourceType: 'module',
38 | ecmaFeatures: {
39 | arrowFunctions: true,
40 | blockBindings: true,
41 | classes: true,
42 | defaultParams: true,
43 | destructuring: true,
44 | forOf: true,
45 | generators: false,
46 | modules: true,
47 | objectLiteralComputedProperties: true,
48 | objectLiteralDuplicateProperties: false,
49 | objectLiteralShorthandMethods: true,
50 | objectLiteralShorthandProperties: true,
51 | spread: true,
52 | superInFunctions: true,
53 | templateStrings: true,
54 | jsx: true
55 | }
56 | },
57 |
58 | rules: {
59 | 'for-direction': 2,
60 | 'no-await-in-loop' : 2,
61 | 'no-compare-neg-zero' : 2,
62 | 'no-cond-assign' : [2, 'always'],
63 | 'no-console' : process.env.NODE_ENV === 'production' ? 'error' : 'off',
64 | 'no-constant-condition' : 1,
65 | 'no-control-regex' : 2,
66 | 'no-debugger' : process.env.NODE_ENV === 'production' ? 'error' : 'off',
67 | 'no-dupe-args' : 2,
68 | 'no-dupe-keys' : 2,
69 | 'no-duplicate-case' : 2,
70 | 'no-empty' : 2,
71 | 'no-empty-character-class' : 2,
72 | 'no-ex-assign' : 2,
73 | 'no-extra-boolean-cast' : 2,
74 | 'no-extra-parens' : [2, 'functions'],
75 | 'no-extra-semi' : 2,
76 | 'no-func-assign' : 2,
77 | 'no-inner-declarations' : 2,
78 | 'no-invalid-regexp' : 2,
79 | 'no-irregular-whitespace' : 2,
80 | 'no-obj-calls' : 2,
81 | 'no-prototype-builtins' : 2,
82 | 'no-regex-spaces' : 2,
83 | 'no-sparse-arrays' : 2,
84 | 'no-template-curly-in-string' : 2,
85 | 'no-unexpected-multiline' : 0,
86 | 'no-unreachable' : 2,
87 | 'no-unsafe-finally' : 2,
88 | 'no-unsafe-negation' : 2,
89 | 'use-isnan' : 2,
90 | 'valid-jsdoc' : 0,
91 | 'valid-typeof' : 2,
92 |
93 | 'accessor-pairs': 2,
94 | 'array-callback-return' : 1,
95 | 'block-scoped-var' : 2,
96 | 'class-methods-use-this' : 0,
97 | complexity : [0, { max: 20 }],
98 | 'consistent-return' : 0,
99 | curly : [2, 'multi-line'],
100 | 'default-case' : 2,
101 | 'dot-location' : [2, 'property'],
102 | 'dot-notation' : [2, { allowKeywords: true }],
103 | eqeqeq : [2, 'smart'],
104 | 'guard-for-in' : 2,
105 | 'no-alert' : 2,
106 | 'no-caller' : 2,
107 | 'no-case-declarations' : 2,
108 | 'no-div-regex' : 2,
109 | 'no-else-return' : 2,
110 | 'no-empty-function' : [2, { allow: ['arrowFunctions', 'constructors'] }],
111 | 'no-empty-pattern' : 2,
112 | 'no-eq-null' : 2,
113 | 'no-eval' : 2,
114 | 'no-extend-native' : 2,
115 | 'no-extra-bind' : 2,
116 | 'no-extra-label' : 2,
117 | 'no-fallthrough' : 2,
118 | 'no-floating-decimal' : 2,
119 | 'no-global-assign' : 2,
120 | 'no-implicit-coercion' : 0,
121 | 'no-implicit-globals' : 0,
122 | 'no-implied-eval' : 2,
123 | 'no-invalid-this' : 0,
124 | 'no-iterator' : 2,
125 | 'no-labels' : 2,
126 | 'no-lone-blocks' : 2,
127 | 'no-loop-func' : 2,
128 | 'no-magic-numbers' : 0,
129 | 'no-multi-spaces' : [2, { exceptions: { Property: true, VariableDeclarator: true, ImportDeclaration: true } }],
130 | 'no-multi-str' : 2,
131 | 'no-new' : 2,
132 | 'no-new-func' : 2,
133 | 'no-new-wrappers' : 2,
134 | 'no-octal' : 2,
135 | 'no-octal-escape' : 2,
136 | 'no-param-reassign' : 2,
137 | 'no-proto' : 2,
138 | 'no-redeclare' : 2,
139 | 'no-restricted-properties' : 0,
140 | 'no-return-assign' : [2, 'always'],
141 | 'no-return-await' : 2,
142 | 'no-script-url' : 2,
143 | 'no-self-assign' : [2, { props: true }],
144 | 'no-self-compare' : 2,
145 | 'no-sequences' : 2,
146 | 'no-throw-literal' : 2,
147 | 'no-unmodified-loop-condition' : 2,
148 | 'no-unused-expressions' : 0,
149 | 'no-unused-labels' : 2,
150 | 'no-useless-call' : 2,
151 | 'no-useless-concat' : 2,
152 | 'no-useless-escape' : 2,
153 | 'no-useless-return' : 2,
154 | 'no-void' : 2,
155 | 'no-warning-comments' : 0,
156 | 'no-with' : 2,
157 | 'prefer-promise-reject-errors' : 2,
158 | radix : 2,
159 | 'require-await' : 2,
160 | 'vars-on-top' : 2,
161 | 'wrap-iife' : [2, 'any'],
162 | yoda : 2,
163 |
164 | strict: [
165 | 2,
166 | 'never'
167 | ],
168 |
169 | 'init-declarations': 0,
170 | 'no-catch-shadow' : 0,
171 | 'no-delete-var' : 2,
172 | 'no-label-var' : 2,
173 | 'no-restricted-globals' : 0,
174 | 'no-shadow' : 2,
175 | 'no-shadow-restricted-names' : 2,
176 | 'no-undef' : 2,
177 | 'no-undef-init' : 2,
178 | 'no-undefined' : 2,
179 | 'no-unused-vars' : [2, { vars: 'local', args: 'none', argsIgnorePattern: '^_' }],
180 | 'no-use-before-define' : [2, { functions: true, classes: true }],
181 |
182 | 'callback-return': 0,
183 | 'global-require' : 0,
184 | 'handle-callback-err' : 2,
185 | 'no-buffer-constructor' : 2,
186 | 'no-mixed-requires' : 0,
187 | 'no-new-require' : 2,
188 | 'no-path-concat' : 0,
189 | 'no-process-env' : 0,
190 | 'no-process-exit' : 0,
191 | 'no-restricted-modules' : 0,
192 | 'no-sync' : 0,
193 |
194 | 'array-bracket-newline' : [2, { multiline: true }],
195 | 'array-bracket-spacing' : 2,
196 | 'array-element-newline' : 0,
197 | 'block-spacing' : [2, 'always'],
198 | 'brace-style' : [2, '1tbs', { allowSingleLine: true }],
199 | camelcase : [2, { properties: 'never' }],
200 | 'capitalized-comments' : 0,
201 | 'comma-dangle' : [2, 'never'],
202 | 'comma-spacing' : [2, { before: false, after: true }],
203 | 'comma-style' : [2, 'last'],
204 | 'computed-property-spacing' : 2,
205 | 'consistent-this' : 0,
206 | 'eol-last' : 2,
207 | 'func-call-spacing' : 2,
208 | 'func-name-matching' : 0,
209 | 'func-names' : 0,
210 | 'func-style' : [2, 'declaration', { allowArrowFunctions: true }],
211 | 'function-paren-newline' : [2, 'consistent'],
212 | 'id-blacklist' : 0,
213 | 'id-length' : 0,
214 | 'id-match' : 0,
215 | indent : [2, 2, { SwitchCase: 1 }],
216 | 'jsx-quotes' : [2, 'prefer-double'],
217 | 'key-spacing' : 0,
218 | 'keyword-spacing' : [2, { before: true, after: true }],
219 | 'line-comment-position' : 0,
220 | 'linebreak-style' : 0,
221 | 'lines-around-comment' : 0,
222 | 'lines-between-class-members' : [2, 'always', { exceptAfterSingleLine: true }],
223 | 'max-depth' : 0,
224 | 'max-len' : 0,
225 | 'max-lines' : 0,
226 | 'max-nested-callbacks' : 0,
227 | 'max-params' : 0,
228 | 'max-statements' : 0,
229 | 'max-statements-per-line' : 0,
230 | 'multiline-comment-style' : 0,
231 | 'multiline-ternary' : [2, 'always-multiline'],
232 | 'new-cap' : 0,
233 | 'new-parens' : 2,
234 | 'newline-per-chained-call' : 0,
235 | 'no-array-constructor' : 0,
236 | 'no-bitwise' : [2, { allow: ['~'] }],
237 | 'no-continue' : 2,
238 | 'no-inline-comments' : 0,
239 | 'no-lonely-if' : 2,
240 | 'no-mixed-operators' : 0,
241 | 'no-mixed-spaces-and-tabs' : [2, 'smart-tabs'],
242 | 'no-multi-assign' : 2,
243 | 'no-multiple-empty-lines' : [2, { max: 2 }],
244 | 'no-negated-condition' : 2,
245 | 'no-nested-ternary' : 0,
246 | 'no-new-object' : 2,
247 | 'no-plusplus' : 0,
248 | 'no-restricted-syntax' : 0,
249 | 'no-tabs' : 0,
250 | 'no-ternary' : 0,
251 | 'no-trailing-spaces' : 2,
252 | 'no-underscore-dangle' : 0,
253 | 'no-unneeded-ternary' : 2,
254 | 'no-whitespace-before-property' : 2,
255 | 'nonblock-statement-body-position' : 0,
256 | 'object-curly-newline' : [2, { ObjectExpression: { consistent: true }, ObjectPattern: { consistent: true } }],
257 | 'object-curly-spacing' : [2, 'always'],
258 | 'object-property-newline' : [2, { allowMultiplePropertiesPerLine: true }],
259 | 'one-var' : [2, 'never'],
260 | 'one-var-declaration-per-line' : [2, 'always'],
261 | 'operator-assignment' : [2, 'always'],
262 | 'operator-linebreak' : [2, 'before', { overrides: { '&&': 'ignore', '=': 'ignore' } }],
263 | 'padded-blocks' : [2, 'never'],
264 | 'padding-line-between-statements' : 0,
265 | 'quote-props' : [2, 'consistent-as-needed'],
266 | quotes : [2, 'backtick', 'avoid-escape'],
267 | 'require-jsdoc' : 0,
268 | semi : [2, 'never'],
269 | 'semi-spacing' : [2, { before: false, after: true }],
270 | 'semi-style' : [2, 'last'],
271 | 'sprt-keys' : 0,
272 | 'sort-vars' : 0,
273 | 'space-before-blocks' : 2,
274 | 'space-before-function-paren' : [2, { anonymous: 'never', named: 'never', asyncArrow: 'always' }],
275 | 'space-in-parens' : [2, 'never'],
276 | 'space-infix-ops' : 2,
277 | 'space-unary-ops' : [2, { words: true, nonwords: false }],
278 | 'spaced-comment' : [0, 'always', { plugins: ['react'], exceptions: ['*'], markers: ['*'] }],
279 | 'switch-colon-spacing' : [2, { before: false, after: true }],
280 | 'template-tag-spacing' : [2, 'never'],
281 | 'unicode-bom' : 0,
282 | 'wrap-regex' : 2,
283 |
284 | 'arrow-body-style': [
285 | 2,
286 | 'as-needed'
287 | ],
288 | 'arrow-parens' : [2, 'as-needed'],
289 | 'arrow-spacing' : [2, { before: true, after: true }],
290 | 'constructor-super' : 0,
291 | 'generator-star-spacing' : [2, { before: true, after: false }],
292 | 'no-class-assign' : 2,
293 | 'no-confusing-arrow' : [2, { allowParens: true }],
294 | 'no-const-assign' : 2,
295 | 'no-dupe-class-members' : 2,
296 | 'no-duplicate-imports' : 2,
297 | 'no-new-symbol' : 2,
298 | 'no-restricted-imports' : 0,
299 | 'no-this-before-super' : 2,
300 | 'no-useless-computed-key' : 2,
301 | 'no-useless-constructor' : 2,
302 | 'no-useless-rename' : 2,
303 | 'no-var' : 2,
304 | 'object-shorthand' : [2, 'properties'],
305 | 'prefer-arrow-callback' : 2,
306 | 'prefer-const' : 0,
307 | 'prefer-destructuring' : 0,
308 | 'prefer-numeric-literals' : 2,
309 | 'prefer-rest-params' : 2,
310 | 'prefer-spread' : 2,
311 | 'prefer-template' : 2,
312 | 'require-yield' : 2,
313 | 'rest-spread-spacing' : [2, 'never'],
314 | 'sort-imports' : 0,
315 | 'symbol-description' : 2,
316 | 'template-curly-spacing' : [2, 'never'],
317 | 'yield-star-spacing' : [2, { before: false, after: true }],
318 |
319 | 'import/no-unresolved': [
320 | 2,
321 | {
322 | commonjs: true,
323 | amd: true
324 | }
325 | ],
326 | 'import/named' : 2,
327 | 'import/default' : 2,
328 | 'import/no-webpack-loader-syntax' : 2,
329 | 'import/export' : 2,
330 | 'import/no-deprecated' : 2,
331 | 'import/no-mutable-exports' : 2,
332 | 'import/no-duplicates' : 2,
333 | 'import/no-namespace' : 0,
334 | 'import/newline-after-import' : 2,
335 | 'import/order' : [2, { 'newlines-between': 'never', groups: ['builtin', ['internal', 'external'], ['parent', 'sibling'], 'index'] }],
336 |
337 | 'promise/catch-or-return': [
338 | 2,
339 | {
340 | terminationMethod: [
341 | 'catch',
342 | 'finally'
343 | ]
344 | }
345 | ],
346 | 'promise/always-return' : 2,
347 | 'promise/no-return-wrap' : 2,
348 | 'promise/param-names' : 2,
349 | 'promise/prefer-await-to-then' : 2,
350 | 'promise/prefer-await-to-callbacks' : 2,
351 | }
352 | }
353 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | /lib/
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | /test/unit/coverage/
9 | /coverage
10 |
11 | # Editor directories and files
12 | .idea
13 | .vscode
14 | *.suo
15 | *.ntvs*
16 | *.njsproj
17 | *.sln
18 |
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "src/**/*.{js,jsx}": [
3 | "prettier --write",
4 | "eslint --fix",
5 | "git add"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": false,
6 | "singleQuote": true,
7 | "trailingComma": "none",
8 | "bracketSpacing": true,
9 | "jsxBracketSameLine": false
10 | }
--------------------------------------------------------------------------------
/.snyk:
--------------------------------------------------------------------------------
1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
2 | version: v1.10.2
3 | ignore: {}
4 | patch: {}
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - 8
5 |
6 | dist: trusty
7 |
8 | sudo: false
9 |
10 | cache:
11 | npm: true
12 | directories:
13 | - node_modules
14 |
15 | before_install:
16 | - yarn global add greenkeeper-lockfile@1
17 |
18 | before_script:
19 | - cd example && yarn && cd ..
20 | - greenkeeper-lockfile-update
21 | - yarn test:coverage
22 |
23 | script:
24 | - yarn build
25 |
26 | after_script:
27 | - greenkeeper-lockfile-upload
28 |
29 | branches:
30 | only:
31 | - master
32 | - /^greenkeeper/.*$/
33 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Drake Costa drake@saeris.io
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Vue Spinners
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | A Vue.js port of react-spinners.
20 |
21 | ## 📦 Installation
22 |
23 | ```bash
24 | npm install --save @saeris/vue-spinners
25 | # or
26 | yarn add @saeris/vue-spinners
27 | ```
28 |
29 | ## 🔧 Usage
30 |
31 | There are a number of ways you can use this library! Here are a few examples:
32 |
33 | **[Vue Plugin](https://vuejs.org/v2/guide/plugins.html#Using-a-Plugin)**
34 | ```js
35 | import Vue from 'vue'
36 | import { VueSpinners } from '@saeris/vue-spinners'
37 |
38 | Vue.use(VueSpinners)
39 |
40 | // Each spinner can now be used in your templates anywhere in the app!
41 | ```
42 |
43 | **[Local Component Registration](https://vuejs.org/v2/guide/components-registration.html#Local-Registration)**
44 | ```js
45 | import { BarLoader } from '@saeris/vue-spinners'
46 |
47 | export default {
48 | components: {
49 | BarLoader
50 | },
51 | // ...
52 | }
53 | ```
54 |
55 | **[JSX Component](https://vuejs.org/v2/guide/render-function.html#JSX)**
56 |
57 | ```js
58 | import { BarLoader } from '@saeris/vue-spinners'
59 |
60 | export default {
61 | data: () => ({
62 | loading: true
63 | }),
64 | render() {
65 | return (
66 |
67 |
74 |
75 | )
76 | }
77 | }
78 | ```
79 |
80 | **[Unpkg Import](https://vuejs.org/v2/cookbook/packaging-sfc-for-npm.html#What-does-my-packaged-component-look-like)**
81 | ```html
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
94 | ```
95 |
96 | ## 📋 Available Loaders, PropTypes, and Default Values
97 |
98 | Common default props for all loaders:
99 |
100 | ```js
101 | loading: true
102 | color: '#000000'
103 | ```
104 |
105 | For `size`, `height`, and `width` props, there are `sizeUnit`, `heightUnit`, and `widthUnit` prop that accepts `px`, `%`, or `em`. The default for the unit prop is `px`.
106 |
107 | Loader | size:int | height:int | width:int | radius:int | margin:str
108 | -----------------------:|:--------:|:----------:|:---------:|:----------:|:---------:
109 | BarLoader | | `4` | `100` | |
110 | BeatLoader | `15` | | | | `2px`
111 | BounceLoader | `60` | | | |
112 | CircleLoader | `50` | | | |
113 | ClimbingBoxLoader | `15` | | | |
114 | ClipLoader | `35` | | | |
115 | DotLoader | `60` | | | | `2px`
116 | FadeLoader | | `15` | `5` | `2` | `2px`
117 | GridLoader | `15` | | | |
118 | HashLoader | `50` | | | | `2px`
119 | MoonLoader | `60` | | | | `2px`
120 | PacmanLoader | `25` | | | | `2px`
121 | PropagateLoader | `15` | | | |
122 | PulseLoader | `15` | | | | `2px`
123 | RingLoader | `60` | | | | `2px`
124 | RiseLoader | `15` | | | | `2px`
125 | RotateLoader | `15` | | | | `2px`
126 | ScaleLoader | | `35` | `4` | `2` | `2px`
127 | SkewLoader | `20` | | | |
128 | SquareLoader | `50` | | | |
129 | SyncLoader | `15` | | | | `2px`
130 |
131 | ## 🏖️ Demo
132 |
133 | You can either visit the [live demo site](https://vue-spinners.saeris.io), clone this repo and run the demo locally using `yarn start` and opening your browser to http://localhost:8080, or you can just play with it inside of CodeSandbox [here](https://codesandbox.io/s/github/Saeris/vue-spinners/tree/master/example).
134 |
135 | ## ⚠️ Support Notice
136 |
137 | This code is released as-is. It was originally built for use with Vue 2.x and as-such it is now very out of date. I do not plan to make continued updates to this package, so if you find it useful then I would highly recommend that you create a fork or copy-and-paste code to suit your own needs. The version of `emotion` that is uses is now very out of date and *will* cause problems when used in a modern codebase alongside another version of `emotion`. I am also unable to provide support or answer questions due to my lack of knowledge of Vuejs.
138 |
139 | ## 📣 Acknowledgements
140 |
141 | This library is a Vue port of [react-spinners](https://github.com/davidhu2000/react-spinners) by [David Hu](https://github.com/davidhu2000), who's library is based on [Halogen](https://github.com/yuanyan/halogen).
142 |
143 | ## 🥂 License
144 |
145 | Released under the [MIT license](https://github.com/Saeris/vue-spinners/blob/master/LICENSE.md).
146 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | `@vue/app`
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | parsers:
3 | javascript:
4 | enable_partials: yes
5 |
--------------------------------------------------------------------------------
/example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "modules": false,
7 | "targets": {
8 | "browsers": [
9 | "> 1%",
10 | "last 2 versions",
11 | "not ie <= 8"
12 | ]
13 | }
14 | }
15 | ],
16 | "stage-2"
17 | ],
18 | "plugins": [
19 | "vue-jsx-sync",
20 | "jsx-vue-functional",
21 | "transform-class-properties",
22 | "syntax-flow",
23 | "transform-flow-strip-types",
24 | "transform-decorators-legacy",
25 | "transform-vue-jsx",
26 | "transform-runtime"
27 | ],
28 | "env": {
29 | "test": {
30 | "presets": [
31 | "env",
32 | "stage-2"
33 | ],
34 | "plugins": [
35 | "vue-jsx-sync",
36 | "jsx-vue-functional",
37 | "transform-class-properties",
38 | "syntax-flow",
39 | "transform-flow-strip-types",
40 | "transform-vue-jsx",
41 | "transform-es2015-modules-commonjs",
42 | "dynamic-import-node"
43 | ]
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | /lib/
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | /test/unit/coverage/
9 |
10 | # Editor directories and files
11 | .idea
12 | .vscode
13 | *.suo
14 | *.ntvs*
15 | *.njsproj
16 | *.sln
17 |
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [`@vue/app`]
3 | }
4 |
--------------------------------------------------------------------------------
/example/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleFileExtensions: [
3 | `js`,
4 | `jsx`,
5 | `json`,
6 | `vue`
7 | ],
8 | transform: {
9 | '^.+\\.vue$': `vue-jest`,
10 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': `jest-transform-stub`,
11 | '^.+\\.jsx?$': `babel-jest`
12 | },
13 | moduleNameMapper: {
14 | '^@/(.*)$': `/src/$1`
15 | },
16 | snapshotSerializers: [`jest-serializer-vue`],
17 | testMatch: [`**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)`],
18 | testURL: `http://localhost/`
19 | }
20 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@saeris/vue-spinner-example",
3 | "version": "1.0.0",
4 | "private": true,
5 | "dependencies": {
6 | "@saeris/vue-spinners": "^1.0.5",
7 | "emotion": "9.2.12",
8 | "v-click-outside": "2.0.1",
9 | "vue": "^2.5.2",
10 | "vue-class-component": "6.3.1",
11 | "vue-color": "2.6.0",
12 | "vue-emotion": "0.4.2"
13 | },
14 | "devDependencies": {
15 | "autoprefixer": "^9.1.5",
16 | "babel-core": "^6.22.1",
17 | "babel-eslint": "^10.0.1",
18 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
19 | "babel-jest": "^23.6.0",
20 | "babel-loader": "^8.0.4",
21 | "babel-plugin-jsx-vue-functional": "2.1.0",
22 | "babel-plugin-syntax-flow": "6.18.0",
23 | "babel-plugin-syntax-jsx": "^6.18.0",
24 | "babel-plugin-transform-class-properties": "6.24.1",
25 | "babel-plugin-transform-decorators-legacy": "1.3.5",
26 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
27 | "babel-plugin-transform-flow-strip-types": "6.22.0",
28 | "babel-plugin-transform-runtime": "^6.22.0",
29 | "babel-plugin-transform-vue-jsx": "^3.5.0",
30 | "babel-plugin-vue-jsx-sync": "0.0.5",
31 | "babel-preset-env": "^1.3.2",
32 | "babel-preset-stage-2": "^6.22.0",
33 | "chalk": "^2.0.1",
34 | "copy-webpack-plugin": "^4.0.1",
35 | "css-loader": "^1.0.0",
36 | "eslint": "^5.6.1",
37 | "eslint-config-airbnb-base": "^13.1.0",
38 | "eslint-friendly-formatter": "^4.0.1",
39 | "eslint-import-resolver-webpack": "^0.10.1",
40 | "eslint-loader": "^2.1.1",
41 | "eslint-plugin-import": "^2.7.0",
42 | "eslint-plugin-vue": "^4.0.0",
43 | "extract-text-webpack-plugin": "^3.0.0",
44 | "file-loader": "^2.0.0",
45 | "flow-bin": "0.82.0",
46 | "friendly-errors-webpack-plugin": "^1.6.1",
47 | "html-webpack-plugin": "^3.2.0",
48 | "jest": "^23.6.0",
49 | "jest-serializer-vue": "^2.0.2",
50 | "node-notifier": "^5.1.2",
51 | "optimize-css-assets-webpack-plugin": "^5.0.1",
52 | "ora": "^3.0.0",
53 | "portfinder": "^1.0.13",
54 | "postcss-import": "^12.0.0",
55 | "postcss-loader": "^3.0.0",
56 | "postcss-url": "^8.0.0",
57 | "rimraf": "^2.6.0",
58 | "semver": "^5.3.0",
59 | "shelljs": "^0.8.2",
60 | "uglifyjs-webpack-plugin": "^2.0.1",
61 | "url-loader": "^1.1.1",
62 | "vue-jest": "^2.6.0",
63 | "vue-loader": "^15.4.2",
64 | "vue-style-loader": "^4.1.2",
65 | "vue-template-compiler": "^2.5.2",
66 | "webpack": "^4.20.2",
67 | "webpack-bundle-analyzer": "^3.0.2",
68 | "webpack-dev-server": "^3.1.9",
69 | "webpack-merge": "^4.1.0"
70 | },
71 | "browserslist": [
72 | "> 1%",
73 | "last 2 versions",
74 | "not ie <= 8"
75 | ],
76 | "lint-staged": {
77 | "*.js": [
78 | "vue-cli-service lint",
79 | "git add"
80 | ],
81 | "*.vue": [
82 | "vue-cli-service lint",
83 | "git add"
84 | ]
85 | },
86 | "repository": {
87 | "type": "git",
88 | "url": "https://github.com/Saeris/vue-spinners"
89 | },
90 | "description": "Vue port of React Spinners",
91 | "license": "MIT",
92 | "engines": {
93 | "node": ">= 6.0.0",
94 | "npm": ">= 3.0.0"
95 | },
96 | "files": [
97 | "dist/*",
98 | "src/*",
99 | "*.json",
100 | "*.js"
101 | ],
102 | "keywords": [
103 | "loading spinners",
104 | "loaders",
105 | "spinners",
106 | "loading indicators"
107 | ],
108 | "gitHooks": {
109 | "pre-commit": "lint-staged"
110 | },
111 | "main": "src/main.js"
112 | }
113 |
--------------------------------------------------------------------------------
/example/src/components/code.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Component from 'vue-class-component'
3 | import styled from "vue-emotion"
4 |
5 | const Container = styled(`section`)`
6 | margin: 50px auto;
7 | color: #fff;
8 | display: -webkit-box;
9 | display: -ms-flexbox;
10 | display: flex;
11 | align-items: center;
12 | cursor: pointer;
13 |
14 | span {
15 | background: #2b303b;
16 | padding: 16px 32px;
17 | font-size: 20px;
18 | margin: 0 auto;
19 | font-family: Courier New, Courier, monospace;
20 | opacity: 0.9;
21 | transition: 0.3s ease-out;
22 |
23 | &:hover {
24 | box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.14),
25 | 0 1px 7px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -1px rgba(0, 0, 0, 0.2);
26 | opacity: 1;
27 | }
28 | }
29 | `
30 |
31 | @Component
32 | export class Code extends Vue {
33 | text = [
34 | `yarn add @saeris/vue-spinners`,
35 | `npm install @saeris/vue-spinners --save`
36 | ]
37 |
38 | index = 0
39 |
40 | handleClick(e) {
41 | e.preventDefault()
42 | this.index = +!this.index
43 | }
44 |
45 | render() {
46 | return (
47 |
48 | {this.text[this.index]}
49 |
50 | )
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/example/src/components/colorPicker.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Component from 'vue-class-component'
3 | import styled from "vue-emotion"
4 | import { Sketch } from 'vue-color'
5 | import vClickOutside from 'v-click-outside'
6 |
7 | Vue.use(vClickOutside)
8 |
9 | const Container = styled(`section`)`
10 | top: ${({ pos }) => `${pos}px`};
11 | left: 20px;
12 | position: fixed;
13 | z-index: 999;
14 | `
15 |
16 | const Button = styled(`button`)`
17 | height: 36px;
18 | font-size: 14px;
19 | line-height: 36px;
20 | background: #2b303b;
21 | color: #fff;
22 | border: none;
23 | opacity: 0.9;
24 | -webkit-transition: 0.5s ease-in-out;
25 | -o-transition: 0.5s ease-in-out;
26 | transition: 0.5s ease-in-out;
27 | font-weight: 300;
28 | text-transform: uppercase;
29 | cursor: pointer;
30 | border-radius: 2px;
31 | letter-spacing: 0.5px;
32 | padding: 0 28px;
33 |
34 | &:hover {
35 | box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.14),
36 | 0 1px 7px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -1px rgba(0, 0, 0, 0.2);
37 | opacity: 1;
38 | }
39 | `
40 |
41 | @Component({
42 | props: {
43 | color: {
44 | type: String,
45 | required: true
46 | },
47 | updateColor: {
48 | type: Function,
49 | required: true
50 | }
51 | }
52 | })
53 | export class ColorPicker extends Vue {
54 | pos = 370
55 | showPicker = false
56 |
57 | beforeMount() {
58 | window.addEventListener(`scroll`, this.updatePosition, true)
59 | }
60 |
61 | beforeDestroy() {
62 | window.removeEventListener(`scorll`, this.updatePosition, true)
63 | }
64 |
65 | updatePosition() {
66 | let top = 370 - (window.scrollY * 2)
67 | if (top > 60) {
68 | this.pos = top
69 | } else {
70 | this.pos = 50
71 | }
72 | }
73 |
74 | togglePicker() {
75 | this.showPicker = !this.showPicker
76 | }
77 |
78 | render() {
79 | return (
80 |
81 | {this.showPicker ? (
82 |
87 | ) : (
88 |
89 | )}
90 |
91 | )
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/example/src/components/form.js:
--------------------------------------------------------------------------------
1 | import styled from "vue-emotion"
2 |
3 | const Container = styled(`div`)`
4 | position: absolute;
5 | bottom: 5px;
6 | font-family: sans-serif, serif;
7 | width: 80%;
8 | display: -webkit-box;
9 | display: -ms-flexbox;
10 | display: flex;
11 | flex-direction: row;
12 | flex-wrap: wrap;
13 | justify-content: center;
14 | `
15 |
16 | const InputGroup = styled(`div`)`
17 | margin: 3px;
18 | display: flex;
19 | width: 50px;
20 | height: 20px;
21 | position: relative;
22 | `
23 |
24 | const Input = styled(`input`)`
25 | width: 50px;
26 | padding: 0 4px;
27 | display: block;
28 | border: none;
29 | border-bottom: 1px solid #757575;
30 | text-align: center;
31 |
32 | &:focus {
33 | outline: none;
34 | }
35 | `
36 |
37 | const Bar = styled(`span`)`
38 | position: absolute;
39 | display: block;
40 | width: 100%;
41 | bottom: -1px;
42 |
43 | &::before {
44 | left: 50%;
45 | }
46 |
47 | &::after {
48 | right: 50%;
49 | }
50 |
51 | &::before,
52 | &::after {
53 | content: '';
54 | height: 2px;
55 | width: 0;
56 | bottom: 1px;
57 | position: absolute;
58 | background: #36d7b7;
59 | transition: 0.2s ease all;
60 | }
61 | `
62 |
63 | const Label = styled(`label`)`
64 | top: -20px;
65 | flex: 1;
66 | font-size: 10px;
67 | line-height: 20px;
68 | text-align: center;
69 | color: #999;
70 | position: absolute;
71 | pointer-events: none;
72 | transition: 0.2s ease all;
73 | width: 50px;
74 | `
75 |
76 | export const Form = {
77 | functional: true,
78 | props: {
79 | inputs: { type: Object, default: () => ({}) },
80 | update: { type: Function, required: true }
81 | },
82 | render(h, { props: { inputs, update } }) {
83 | return (
84 |
85 | {Object.keys(inputs).map(name => (
86 |
87 |
93 |
94 |
95 |
96 | ))}
97 |
98 | )
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/example/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { Code } from './code'
2 | export { ColorPicker } from './colorPicker'
3 | export { Layout } from './layout'
4 | export { LoaderItem } from './loaderItem'
5 |
--------------------------------------------------------------------------------
/example/src/components/layout.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Component from 'vue-class-component'
3 | import styled from 'vue-emotion'
4 |
5 | const Main = styled(`main`)`
6 | .spinners {
7 | margin-top: ${({ isFixed }) => `${isFixed ? 250 : 0}px`}
8 | }
9 | `
10 |
11 | const Header = styled(`header`)`
12 | position: realtive;
13 | display: flex;
14 | align-items: center;
15 | width: 100%;
16 | height: ${({ height }) => `${height}px`};
17 | color: #fff;
18 | z-index: 1000;
19 | background: linear-gradient(90deg, ${({ color }) => color}, #2b303b);
20 |
21 | &.fixed {
22 | position: fixed;
23 | top: 0;
24 | box-shadow: rgba(0, 0, 0, 0.14) 0px 3px 3px 0px,
25 | rgba(0, 0, 0, 0.12) 0px 1px 7px 0px, rgba(0, 0, 0, 0.2) 0px 3px 1px -1px;
26 | }
27 | `
28 |
29 | const Logo = styled(`h1`)`
30 | height: 50px;
31 | line-height: 50px;
32 | display: block;
33 | text-align: center;
34 | margin: 0px auto;
35 | font-size: 40px;
36 | letter-spacing: 5px;
37 | font-weight: 300;
38 | text-transform: uppercase;
39 | position: -webkit-sticky;
40 | position: sticky;
41 | top: 10px;
42 | transition: 0.2s ease-in-out;
43 |
44 | &.fixed {
45 | font-size: 20px;
46 | }
47 | `
48 |
49 | const Footer = styled(`footer`)`
50 | padding: 30px;
51 | margin: 15px 0 0 0;
52 | background: #2b303b;
53 | text-align: center;
54 |
55 | a {
56 | text-decoration: none;
57 | color: #ccc;
58 | }
59 | `
60 |
61 | const Ribbon = styled(`img`)`
62 | position: absolute;
63 | top: 0;
64 | right: 0;
65 | border: 0;
66 | `
67 | @Component({
68 | props: {
69 | color: {
70 | type: String,
71 | required: true
72 | }
73 | }
74 | })
75 | export class Layout extends Vue {
76 | isFixed = false
77 | height = 360
78 |
79 | beforeMount() {
80 | window.addEventListener(`scroll`, this.handleScroll, true)
81 | }
82 |
83 | beforeDestroy() {
84 | window.removeEventListener(`scorll`, this.handleScroll, true)
85 | }
86 |
87 | handleScroll() {
88 | let height = 360 - window.scrollY
89 | if (height > 200) {
90 | this.height = height
91 | this.isFixed = false
92 | } else {
93 | this.height = 40
94 | this.isFixed = true
95 | }
96 | }
97 |
98 | render() {
99 | return (
100 |
101 |
102 | {this.isFixed
103 | ? null
104 | : (
105 |
106 |
110 |
111 | )}
112 | Vue Spinners
113 |
114 | {this.$slots.default}
115 |
118 |
119 | )
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/example/src/components/loaderItem.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Component from 'vue-class-component'
3 | import styled from "vue-emotion"
4 | import { Form } from './form'
5 |
6 | const Item = styled(`li`)`
7 | flex: 0 0 auto;
8 | display: flex;
9 | flex-direction: column;
10 | align-items: center;
11 | justify-content: center;
12 | padding: 20px;
13 | border: 0.5px solid #e2e2e2;
14 | box-shadow: 1px 1px 1px #e2e2e2;
15 | width: 300px;
16 | height: 300px;
17 | margin: 30px;
18 | font-size: 18px;
19 | letter-spacing: 1px;
20 | position: relative;
21 | `
22 |
23 | const Title = styled(`span`)`
24 | position: absolute;
25 | top: 20px;
26 | `
27 |
28 | @Component({
29 | props: {
30 | spinner: {
31 | type: Object,
32 | required: true
33 | },
34 | name: {
35 | type: String,
36 | required: true
37 | },
38 | color: {
39 | type: String,
40 | required: true
41 | }
42 | }
43 | })
44 | export class LoaderItem extends Vue {
45 | defaults = {}
46 |
47 | beforeMount() {
48 | const props = Object.entries(this.spinner.props).filter(
49 | ([key, values]) => values.default
50 | )
51 | const defaults = props.reduce((hash, [key, values]) => {
52 | hash[key] = values.default
53 | return hash
54 | }, {})
55 | const propTypes = props.reduce((hash, [key, values]) => {
56 | hash[key] = values.type
57 | return hash
58 | }, {})
59 |
60 | delete defaults.color
61 | delete defaults.loading
62 | delete defaults.sizeUnit
63 | delete defaults.widthUnit
64 | delete defaults.heightUnit
65 | delete defaults.radiusUnit
66 | delete defaults.loaderStyle
67 |
68 | this.defaults = defaults
69 | this.propTypes = propTypes
70 | }
71 |
72 | update(field) {
73 | return e => {
74 | this.defaults[field] = this.propTypes[field](e.target.value)
75 | }
76 | }
77 |
78 | renderSpinner(Spinner) {
79 | return (
80 |
88 | )
89 | }
90 |
91 | render() {
92 | return (
93 | -
94 | {this.name}
95 | {this.renderSpinner(this.spinner)}
96 |
97 |
98 | )
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/example/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue"
2 | import Component from "vue-class-component"
3 | import Components from "@saeris/vue-spinners"
4 | import styled from "vue-emotion"
5 | import { Code, ColorPicker, Layout, LoaderItem } from "./components"
6 | import "./styles/index.scss"
7 |
8 | const Spinners = styled(`section`)`
9 | display: flex;
10 | flex-direction: column;
11 | justify-content: center;
12 | min-width: 300px;
13 | width: 80%;
14 | margin: 0 auto;
15 | `
16 |
17 | const List = styled(`ul`)`
18 | display: flex;
19 | flex-wrap: wrap;
20 | justify-content: space-around;
21 | background: #fff;
22 | `
23 |
24 | @Component
25 | class App extends Vue {
26 | color = `#36D7B7`
27 |
28 | updateColor(color) {
29 | this.color = color.hex
30 | }
31 |
32 | renderSpinner(Spinner) {
33 | return
34 | }
35 |
36 | render() {
37 | return (
38 |
39 |
40 |
41 |
42 |
43 | {Object.entries(Components).map(([name, spinner]) => (
44 |
45 | ))}
46 |
47 |
48 |
49 | )
50 | }
51 | }
52 |
53 | // eslint-disable-next-line
54 | new Vue({
55 | el: `#app`,
56 | render: h => h(App)
57 | })
58 |
--------------------------------------------------------------------------------
/example/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | textarea,
6 | input,
7 | button {
8 | outline: none;
9 | }
10 |
11 | input:-webkit-autofill {
12 | box-shadow: 0 0 0 30px white inset;
13 | }
14 |
15 | html,
16 | body {
17 | padding: 0;
18 | margin: 0;
19 | font-family: 'Roboto', sans-serif;
20 | -webkit-font-smoothing: antialiased;
21 | -moz-osx-font-smoothing: grayscale;
22 | background: #e2e2e2;
23 |
24 | button {
25 | border: none;
26 | background: none;
27 |
28 | &:active {
29 | outline: none;
30 | }
31 |
32 | &:focus {
33 | outline: none;
34 | }
35 | }
36 |
37 | ul {
38 | padding: 0;
39 | margin: 0;
40 | list-style: none;
41 |
42 | li {
43 | width: auto;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/greenkeeper.json:
--------------------------------------------------------------------------------
1 | {
2 | "groups": {
3 | "default": {
4 | "packages": [
5 | "example/package.json",
6 | "package.json"
7 | ]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleFileExtensions: [
3 | 'js',
4 | 'jsx',
5 | 'json',
6 | 'vue'
7 | ],
8 | transform: {
9 | '^.+\\.vue$': 'vue-jest',
10 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
11 | '^.+\\.jsx?$': 'babel-jest'
12 | },
13 | moduleNameMapper: {
14 | '^@/(.*)$': '/src/$1'
15 | },
16 | snapshotSerializers: [
17 | 'jest-serializer-vue'
18 | ],
19 | testMatch: [
20 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
21 | ],
22 | testURL: 'http://localhost/'
23 | }
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@saeris/vue-spinners",
3 | "version": "1.0.8",
4 | "description": "Vue port of React Spinners",
5 | "license": "MIT",
6 | "keywords": [
7 | "loading spinners",
8 | "loaders",
9 | "spinners",
10 | "loading indicators"
11 | ],
12 | "main": "./lib/@saeris/vue-spinners.common.js",
13 | "unpkg": "./lib/@saeris/vue-spinners.umd.min.js",
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/Saeris/vue-spinners"
17 | },
18 | "engines": {
19 | "node": ">= 6.0.0",
20 | "npm": ">= 3.0.0"
21 | },
22 | "scripts": {
23 | "start": "vue-cli-service serve ./example/src/main.js",
24 | "build": "yarn build:demo && yarn build:lib",
25 | "build:demo": "vue-cli-service build --dest ./example/dist --modern --target app --name demo ./example/src/main.js",
26 | "build:lib": "vue-cli-service build --dest lib --target lib ./src/index.js",
27 | "lint": "vue-cli-service lint",
28 | "test": "yarn lint && vue-cli-service test:unit",
29 | "test:watch": "yarn test --watch",
30 | "test:coverage": "yarn test --coverage && codecov",
31 | "precommit": "lint-staged",
32 | "prepublish": "snyk protect",
33 | "prepublishOnly": "yarn test && yarn build:lib",
34 | "release": "yarn publish --access public"
35 | },
36 | "peerDependencies": {
37 | "emotion": "9.2.6",
38 | "vue": "^2.5.2",
39 | "vue-emotion": "0.4.2"
40 | },
41 | "devDependencies": {
42 | "@vue/cli": "^3.0.1",
43 | "@vue/cli-plugin-babel": "^3.0.1",
44 | "@vue/cli-plugin-eslint": "^3.0.1",
45 | "@vue/cli-plugin-unit-jest": "^3.0.1",
46 | "@vue/cli-service": "^3.0.1",
47 | "@vue/test-utils": "^1.0.0-beta.20",
48 | "babel-core": "7.0.0-bridge.0",
49 | "babel-jest": "^23.6.0",
50 | "codecov": "^3.1.0",
51 | "emotion": "9.2.12",
52 | "eslint": "^5.6.1",
53 | "eslint-friendly-formatter": "^4.0.1",
54 | "eslint-import-resolver-webpack": "^0.10.1",
55 | "eslint-loader": "^2.1.1",
56 | "eslint-plugin-import": "^2.7.0",
57 | "eslint-plugin-jest": "^21.21.0",
58 | "eslint-plugin-promise": "^4.0.0",
59 | "eslint-plugin-vue": "^4.7.1",
60 | "jest": "^23.6.0",
61 | "lint-staged": "^7.2.2",
62 | "node-sass": "^4.9.3",
63 | "sass-loader": "^7.1.0",
64 | "snyk": "^1.100.1",
65 | "vue": "^2.5.2",
66 | "vue-emotion": "0.4.2",
67 | "vue-template-compiler": "^2.5.17"
68 | },
69 | "browserslist": [
70 | "> 1%",
71 | "last 2 versions",
72 | "not ie <= 8"
73 | ],
74 | "files": [
75 | "lib/*",
76 | "src/*",
77 | "*.json",
78 | "*.js"
79 | ],
80 | "gitHooks": {
81 | "pre-commit": "lint-staged"
82 | },
83 | "lint-staged": {
84 | "*.js": [
85 | "vue-cli-service lint",
86 | "git add"
87 | ],
88 | "*.vue": [
89 | "vue-cli-service lint",
90 | "git add"
91 | ]
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | manifest="<%= htmlWebpackPlugin.files.manifest %>"<% } %>>
7 |
8 |
9 | Vue Spinners
10 |
11 | <% if (htmlWebpackPlugin.files.favicon) { %>
12 |
13 | <% } %>
14 |
15 |
16 | <% for (var css in htmlWebpackPlugin.files.css) { %>
17 |
18 | <% } %>
19 |
20 |
21 |
22 |
23 | <% if (htmlWebpackPlugin.options.window) { %>
24 |
29 | <% } %>
30 |
31 | <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
32 |
33 | <% } %>
34 |
35 | <% if (htmlWebpackPlugin.options.devServer) { %>
36 |
37 | <% } %>
38 |
39 | <% if (htmlWebpackPlugin.options.googleAnalytics) { %>
40 |
52 | <% } %>
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/components/__tests__/barLoader.spec.js:
--------------------------------------------------------------------------------
1 | import { mount } from '@vue/test-utils'
2 | import { BarLoader } from '../barLoader.js'
3 |
4 | describe(`Bar Loader`, () => {
5 | it(`renders props.color when passed`, () => {
6 | const color = `#bada55`
7 | const wrapper = mount(BarLoader, {
8 | context: {
9 | props: { color }
10 | }
11 | })
12 | expect(wrapper.attributes().color).toBe(color)
13 | })
14 |
15 | it(`renders a div`, () => {
16 | const wrapper = mount(BarLoader)
17 | expect(wrapper.contains(`div`)).toBe(true)
18 | })
19 | })
20 |
--------------------------------------------------------------------------------
/src/components/barLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { calculateRgba, range } from '../utils'
3 |
4 | const long = keyframes`
5 | 0% {left: -35%;right: 100%}
6 | 60% {left: 100%;right: -90%}
7 | 100% {left: 100%;right: -90%}
8 | `
9 |
10 | const short = keyframes`
11 | 0% {left: -200%;right: 100%}
12 | 60% {left: 107%;right: -8%}
13 | 100% {left: 107%;right: -8%}
14 | `
15 |
16 | const Wrapper = styled(`div`)`
17 | position: relative;
18 | width: ${({ width, widthUnit }) => `${width}${widthUnit}`};
19 | height: ${({ height, heightUnit }) => `${height}${heightUnit}`};
20 | overflow: hidden;
21 | background-color: ${({ color }) => calculateRgba(color, 0.2)};
22 | background-clip: padding-box;
23 | `
24 |
25 | const Bar = styled(`div`)`
26 | position: absolute;
27 | height: ${({ height, heightUnit }) => `${height}${heightUnit}`};
28 | overflow: hidden;
29 | background-color: ${({ color }) => color};
30 | background-clip: padding-box;
31 | display: block;
32 | border-radius: 2px;
33 | will-change: left, right;
34 | animation-fill-mode: forwards;
35 | animation: ${({ version }) => ` ${(version === 1 ? long : short)} 2.1s ${(version === 2 ? `1.15s` : ``)} ${(version === 1 ? `cubic-bezier(0.65, 0.815, 0.735, 0.395)` : `cubic-bezier(0.165, 0.84, 0.44, 1)`)} infinite`};
36 | `
37 |
38 | export const BarLoader = {
39 | functional: true,
40 | props: {
41 | loading: { type: Boolean, default: true },
42 | color: { type: String, default: `#000000` },
43 | width: { type: Number, default: 100 },
44 | widthUnit: { type: String, default: `px` },
45 | height: { type: Number, default: 4 },
46 | heightUnit: { type: String, default: `px` }
47 | },
48 | render(h, { props, data }) {
49 | return props.loading ? (
50 |
58 | {range(2, 1).map(i => (
59 |
65 | ))}
66 |
67 | ) : null
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/components/beatLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const beat = keyframes`
5 | 50% {transform: scale(0.75);opacity: 0.2}
6 | 100% {transform: scale(1);opacity: 1}
7 | `
8 |
9 | const Circle = styled(`div`)`
10 | display: inline-block;
11 | background-color: ${({ color }) => color};
12 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
13 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
14 | margin: ${({ margin }) => margin};
15 | border-radius: 100%;
16 | animation: ${({ version }) => `${beat} 0.7s ${(version % 2 ? `0s` : `0.35s`)} infinite linear`};
17 | animation-fill-mode: both;
18 | `
19 |
20 | export const BeatLoader = {
21 | functional: true,
22 | props: {
23 | loading: { type: Boolean, default: true },
24 | color: { type: String, default: `#000000` },
25 | size: { type: Number, default: 15 },
26 | sizeUnit: { type: String, default: `px` },
27 | margin: { type: String, default: `2px` }
28 | },
29 | render(h, { props, data }) {
30 | return props.loading ? (
31 |
32 | {range(3, 1).map(i => (
33 |
40 | ))}
41 |
42 | ) : null
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/bounceLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const bounce = keyframes`
5 | 0%, 100% {transform: scale(0)}
6 | 50% {transform: scale(1.0)}
7 | `
8 |
9 | const Wrapper = styled(`div`)`
10 | position: relative;
11 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
12 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
13 | `
14 |
15 | const Circle = styled(`div`)`
16 | position: absolute;
17 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
18 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
19 | background-color: ${({ color }) => color};
20 | border-radius: 100%;
21 | opacity: 0.6;
22 | top: 0;
23 | left: 0;
24 | animation-fill-mode: both;
25 | animation: ${({ version }) => `${bounce} 2.1s ${(version === 1 ? `1s` : `0s`)} infinite ease-in-out`};
26 | `
27 |
28 | export const BounceLoader = {
29 | functional: true,
30 | props: {
31 | loading: { type: Boolean, default: true },
32 | color: { type: String, default: `#000000` },
33 | size: { type: Number, default: 60 },
34 | sizeUnit: { type: String, default: `px` }
35 | },
36 | render(h, { props, data }) {
37 | return props.loading ? (
38 |
43 | {range(2, 1).map(i => (
44 |
50 | ))}
51 |
52 | ) : null
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/circleLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const circle = keyframes`
5 | 0% {transform: rotate(0deg)}
6 | 50% {transform: rotate(180deg)}
7 | 100% {transform: rotate(360deg)}
8 | `
9 |
10 | const Wrapper = styled(`div`)`
11 | position: relative;
12 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
13 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
14 | `
15 |
16 | const Ring = styled(`div`)`
17 | position: absolute;
18 | height: ${({ size, sizeUnit, version }) => `${size * (1 - version / 10)}${sizeUnit}`};
19 | width: ${({ size, sizeUnit, version }) => `${size * (1 - version / 10)}${sizeUnit}`};
20 | border: ${({ color }) => `1px solid ${color}`};
21 | border-radius: 100%;
22 | transition: 2s;
23 | border-bottom: none;
24 | border-right: none;
25 | top: ${({ version }) => version * 0.7 * 2.5}%;
26 | left: ${({ version }) => version * 0.35 * 2.5}%;
27 | animation-fill-mode: '';
28 | animation: ${({ version }) => `${circle} 1s ${version * 0.2}s infinite linear`};
29 | `
30 |
31 | export const CircleLoader = {
32 | functional: true,
33 | props: {
34 | loading: { type: Boolean, default: true },
35 | color: { type: String, default: `#000000` },
36 | size: { type: Number, default: 50 },
37 | sizeUnit: { type: String, default: `px` }
38 | },
39 | render(h, { props, data }) {
40 | return props.loading ? (
41 |
46 | {range(5).map(i => (
47 |
53 | ))}
54 |
55 | ) : null
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/climbingBoxLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 |
3 | const climbingBox = keyframes`
4 | 0% {transform:translate(0, -1em) rotate(-45deg)}
5 | 5% {transform:translate(0, -1em) rotate(-50deg)}
6 | 20% {transform:translate(1em, -2em) rotate(47deg)}
7 | 25% {transform:translate(1em, -2em) rotate(45deg)}
8 | 30% {transform:translate(1em, -2em) rotate(40deg)}
9 | 45% {transform:translate(2em, -3em) rotate(137deg)}
10 | 50% {transform:translate(2em, -3em) rotate(135deg)}
11 | 55% {transform:translate(2em, -3em) rotate(130deg)}
12 | 70% {transform:translate(3em, -4em) rotate(217deg)}
13 | 75% {transform:translate(3em, -4em) rotate(220deg)}
14 | 100% {transform:translate(0, -1em) rotate(-225deg)}
15 | `
16 |
17 | const Container = styled(`div`)`
18 | position: relative;
19 | width: 7.1em;
20 | height: 7.1em;
21 | `
22 |
23 | const Wrapper = styled(`div`)`
24 | position: absolute;
25 | top: 50%;
26 | left: 50%;
27 | margin-top: -2.7em;
28 | margin-left: -2.7em;
29 | width: 5.4em;
30 | height: 5.4em;
31 | font-size: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
32 | `
33 |
34 | const Box = styled(`div`)`
35 | position: absolute;
36 | left: 0;
37 | bottom: -0.1em;
38 | height: 1em;
39 | width: 1em;
40 | background-color: transparent;
41 | border-radius: 15%;
42 | border: ${({ color }) => `0.25em solid ${color}`};
43 | transform: translate(0, -1em) rotate(-45deg);
44 | animation-fill-mode: both;
45 | animation: ${climbingBox} 2.5s infinite cubic-bezier(0.79, 0, 0.47, 0.97);
46 | `
47 |
48 | const Hill = styled(`div`)`
49 | position: absolute;
50 | width: 7.1em;
51 | height: 7.1em;
52 | top: 1.7em;
53 | left: 1.7em;
54 | border-left: ${({ color }) => `0.25em solid ${color}`};
55 | transform: rotate(45deg);
56 | `
57 |
58 | export const ClimbingBoxLoader = {
59 | functional: true,
60 | props: {
61 | loading: { type: Boolean, default: true },
62 | color: { type: String, default: `#000000` },
63 | size: { type: Number, default: 15 },
64 | sizeUnit: { type: String, default: `px` }
65 | },
66 | render(h, { props, data }) {
67 | return props.loading ? (
68 |
69 |
74 |
75 |
76 |
77 |
78 | ) : null
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/clipLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 |
3 | const clip = keyframes`
4 | 0% {transform: rotate(0deg) scale(1)}
5 | 50% {transform: rotate(180deg) scale(0.8)}
6 | 100% {transform: rotate(360deg) scale(1)}
7 | `
8 |
9 | const Ring = styled(`div`)`
10 | background: transparent !important;
11 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
12 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
13 | border-radius: 100%;
14 | border: 2px solid;
15 | border-color: ${({ color }) => color};
16 | border-bottom-color: transparent;
17 | display: inline-block;
18 | animation: ${clip} 0.75s 0s infinite linear;
19 | animation-fill-mode: both;
20 | `
21 |
22 | export const ClipLoader = {
23 | functional: true,
24 | props: {
25 | loading: { type: Boolean, default: true },
26 | color: { type: String, default: `#000000` },
27 | size: { type: Number, default: 35 },
28 | sizeUnit: { type: String, default: `px` }
29 | },
30 | render(h, { props, data }) {
31 | return props.loading ? (
32 |
38 | ) : null
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/dotLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const rotate = keyframes`
5 | 100% {transform: rotate(360deg)}
6 | `
7 |
8 | const bounce = keyframes`
9 | 0%, 100% {transform: scale(0)}
10 | 50% {transform: scale(1.0)}
11 | `
12 |
13 | const Wrapper = styled(`div`)`
14 | position: relative;
15 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
16 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
17 | animation-fill-mode: forwards;
18 | animation: ${rotate} 2s 0s infinite linear;
19 | `
20 |
21 | const Circle = styled(`div`)`
22 | position: absolute;
23 | top: ${({ version }) => (version % 2 ? `0` : `auto`)};
24 | bottom: ${({ version }) => (version % 2 ? `auto` : `0`)};
25 | height: ${({ size, sizeUnit }) => `${size / 2}${sizeUnit}`};
26 | width: ${({ size, sizeUnit }) => `${size / 2}${sizeUnit}`};
27 | background-color: ${({ color }) => color};
28 | border-radius: 100%;
29 | animation-fill-mode: forwards;
30 | animation: ${({ version }) => `${bounce} 2s ${(version === 2 ? `-1s` : `0s`)} infinite linear`};
31 | `
32 |
33 | export const DotLoader = {
34 | functional: true,
35 | props: {
36 | loading: { type: Boolean, default: true },
37 | color: { type: String, default: `#000000` },
38 | size: { type: Number, default: 60 },
39 | sizeUnit: { type: String, default: `px` }
40 | },
41 | render(h, { props, data }) {
42 | return props.loading ? (
43 |
48 | {range(2, 1).map(i => (
49 |
55 | ))}
56 |
57 | ) : null
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/fadeLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { css, keyframes } from 'vue-emotion'
2 | import { characterRange, range, zip } from '../utils'
3 |
4 | const fade = keyframes`
5 | 50% {opacity: 0.3}
6 | 100% {opacity: 1}
7 | `
8 |
9 | const rad = 20
10 | const quarter = rad / 2 + rad / 5.5
11 |
12 | const Wrapper = styled(`div`)`
13 | position: relative;
14 | font-size: 0;
15 | top: ${rad}px;
16 | left: ${rad}px;
17 | width: ${rad * 3}px;
18 | height: ${rad * 3}px;
19 | `
20 |
21 | const Bar = styled(`div`)`
22 | position: absolute;
23 | width: ${({ width, widthUnit }) => `${width}${widthUnit}`};
24 | height: ${({ height, heightUnit }) => `${height}${heightUnit}`};
25 | margin: ${({ margin }) => margin};
26 | background-color: ${({ color }) => color};
27 | border-radius: ${({ radius, radiusUnit }) => `${radius}${radiusUnit}`};
28 | transition: 2s;
29 | animation-fill-mode: 'both';
30 | animation: ${({ version }) => `${fade} 1.2s ${version * 0.12}s infinite ease-in-out`};
31 | ${({ variation }) => variation}
32 | `
33 |
34 | const styles = {
35 | a: css`
36 | top: ${rad}px;
37 | left: 0;
38 | `,
39 | b: css`
40 | top: ${quarter}px;
41 | left: ${quarter}px;
42 | transform: rotate(-45deg);
43 | `,
44 | c: css`
45 | top: 0;
46 | left: ${rad}px;
47 | transform: rotate(90deg);
48 | `,
49 | d: css`
50 | top: ${-quarter}px;
51 | left: ${quarter}px;
52 | transform: rotate(45deg);
53 | `,
54 | e: css`
55 | top: ${-rad}px;
56 | left: 0;
57 | `,
58 | f: css`
59 | top: ${-quarter}px;
60 | left: ${-quarter}px;
61 | transform: rotate(-45deg);
62 | `,
63 | g: css`
64 | top: 0;
65 | left: ${-rad}px;
66 | transform: rotate(90deg);
67 | `,
68 | h: css`
69 | top: ${quarter}px;
70 | left: ${-quarter}px;
71 | transform: rotate(45deg);
72 | `
73 | }
74 |
75 | const rows = zip(characterRange(`a`, `i`).split(``), range(9, 1))
76 |
77 | export const FadeLoader = {
78 | functional: true,
79 | props: {
80 | loading: { type: Boolean, default: true },
81 | color: { type: String, default: `#000000` },
82 | height: { type: Number, default: 15 },
83 | width: { type: Number, default: 5 },
84 | margin: { type: String, default: `2px` },
85 | radius: { type: Number, default: 2 },
86 | widthUnit: { type: String, default: `px` },
87 | heightUnit: { type: String, default: `px` },
88 | radiusUnit: { type: String, default: `px` }
89 | },
90 | render(h, { props, data }) {
91 | return props.loading ? (
92 |
93 | {rows.map(([style, i]) => (
94 |
106 | ))}
107 |
108 | ) : null
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/components/gridLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const grid = keyframes`
5 | 0% {transform: scale(1)}
6 | 50% {transform: scale(0.5); opacity: 0.7}
7 | 100% {transform: scale(1);opacity: 1}
8 | `
9 |
10 | const random = top => Math.random() * top
11 |
12 | const Wrapper = styled(`div`)`
13 | width: ${({ margin, size, sizeUnit }) => `${parseFloat(size) * 3 + parseFloat(margin) * 6}${sizeUnit}`};
14 | font-size: 0;
15 | `
16 |
17 | const Circle = styled(`div`)`
18 | display: inline-block;
19 | background-color: ${({ color }) => color};
20 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
21 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
22 | margin: ${({ margin }) => margin};
23 | border-radius: 100%;
24 | animation-fill-mode: 'both';
25 | animation: ${({ rand }) => `${grid} ${rand / 100 + 0.6}s ${rand / 100 - 0.2}s infinite ease`};
26 | `
27 |
28 | export const GridLoader = {
29 | functional: true,
30 | props: {
31 | loading: { type: Boolean, default: true },
32 | color: { type: String, default: `#000000` },
33 | size: { type: Number, default: 15 },
34 | margin: { type: String, default: `2px` },
35 | sizeUnit: { type: String, default: `px` }
36 | },
37 | render(h, { props, data }) {
38 | return props.loading ? (
39 |
45 | {range(9).map(_ => (
46 |
53 | ))}
54 |
55 | ) : null
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/hashLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { calculateRgba, range } from '../utils'
3 |
4 | const thickness = size => size / 5
5 | const lat = size => (size - thickness(size)) / 2
6 | const offset = size => lat(size) - thickness(size)
7 | const getColor = color => calculateRgba(color, 0.75)
8 |
9 | const before = (size, sizeUnit, color) => keyframes`
10 | 0% {width: ${thickness(size)}px;box-shadow: ${lat(size)}px ${-offset(
11 | size
12 | )}px ${getColor(color)}, ${-lat(size)}px ${offset(size)}px ${getColor(color)}}
13 | 35% {width: ${`${size}${sizeUnit}`};box-shadow: 0 ${-offset(
14 | size
15 | )}px ${getColor(color)}, 0 ${offset(size)}px ${getColor(color)}}
16 | 70% {width: ${thickness(size)}px;box-shadow: ${-lat(size)}px ${-offset(
17 | size
18 | )}px ${getColor(color)}, ${lat(size)}px ${offset(size)}px ${getColor(color)}}
19 | 100% {box-shadow: ${lat(size)}px ${-offset(size)}px ${getColor(
20 | color
21 | )}, ${-lat(size)}px ${offset(size)}px ${getColor(color)}}
22 | `
23 |
24 | const after = (size, sizeUnit, color) => keyframes`
25 | 0% {height: ${thickness(size)}px;box-shadow: ${offset(size)}px ${lat(
26 | size
27 | )}px ${getColor(color)}, ${-offset(size)}px ${-lat(size)}px ${getColor(color)}}
28 | 35% {height: ${`${size}${sizeUnit}`};box-shadow: ${offset(
29 | size
30 | )}px 0 ${getColor(color)}, ${-offset(size)}px 0 ${getColor(color)}}
31 | 70% {height: ${thickness(size)}px;box-shadow: ${offset(size)}px ${-lat(
32 | size
33 | )}px ${getColor(color)}, ${-offset(size)}px ${lat(size)}px ${getColor(color)}}
34 | 100% {box-shadow: ${offset(size)}px ${lat(size)}px ${getColor(
35 | color
36 | )}, ${-offset(size)}px ${-lat(size)}px ${getColor(color)}}
37 | `
38 |
39 | const Wrapper = styled(`div`)`
40 | position: relative;
41 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
42 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
43 | transform: rotate(165deg);
44 | `
45 |
46 | const Lines = styled(`div`)`
47 | position: absolute;
48 | top: 50%;
49 | left: 50%;
50 | display: block;
51 | width: ${({ size, sizeUnit }) => `${size / 5}${sizeUnit}`};
52 | height: ${({ size, sizeUnit }) => `${size / 5}${sizeUnit}`};
53 | border-radius: ${({ size, sizeUnit }) => `${size / 10}${sizeUnit}`};
54 | transform: translate(-50%, -50%);
55 | animation: ${({ size, sizeUnit, color, version }) => `${(version === 1 ? before(size, sizeUnit, color) : after(size, sizeUnit, color))} 2s infinite normal none running`};
56 | content: '';
57 | `
58 |
59 | export const HashLoader = {
60 | functional: true,
61 | props: {
62 | loading: { type: Boolean, default: true },
63 | color: { type: String, default: `#000000` },
64 | size: { type: Number, default: 50 },
65 | sizeUnit: { type: String, default: `px` }
66 | },
67 | render(h, { props, data }) {
68 | return props.loading ? (
69 |
74 | {range(2, 1).map(i => (
75 |
81 | ))}
82 |
83 | ) : null
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { BarLoader } from './barLoader'
2 | export { BeatLoader } from './beatLoader'
3 | export { BounceLoader } from './bounceLoader'
4 | export { CircleLoader } from './circleLoader'
5 | export { ClimbingBoxLoader } from './climbingBoxLoader'
6 | export { ClipLoader } from './clipLoader'
7 | export { DotLoader } from './dotLoader'
8 | export { FadeLoader } from './fadeLoader'
9 | export { GridLoader } from './gridLoader'
10 | export { HashLoader } from './hashLoader'
11 | export { MoonLoader } from './moonLoader'
12 | export { PacmanLoader } from './pacmanLoader'
13 | export { PropagateLoader } from './propagateLoader'
14 | export { PulseLoader } from './pulseLoader'
15 | export { RingLoader } from './ringLoader'
16 | export { RiseLoader } from './riseLoader'
17 | export { RotateLoader } from './rotateLoader'
18 | export { ScaleLoader } from './scaleLoader'
19 | export { SkewLoader } from './skewLoader'
20 | export { SquareLoader } from './squareLoader'
21 | export { SyncLoader } from './syncLoader'
22 |
--------------------------------------------------------------------------------
/src/components/moonLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { css, keyframes } from 'vue-emotion'
2 |
3 | const moon = keyframes`
4 | 100% {transform: rotate(360deg)}
5 | `
6 |
7 | const moonSize = size => (size / 7).toFixed(5)
8 |
9 | const ballStyle = (size, sizeUnit) => css`
10 | width: ${`${size}${sizeUnit}`};
11 | height: ${`${size}${sizeUnit}`};
12 | border-radius: 100%;
13 | `
14 |
15 | const Wrapper = styled(`div`)`
16 | position: relative;
17 | width: ${({ size, sizeUnit }) => `${size + moonSize(size) * 2}${sizeUnit}`};
18 | height: ${({ size, sizeUnit }) => `${size + moonSize(size) * 2}${sizeUnit}`};
19 | animation: ${moon} 0.6s linear 0s infinite normal forwards running;
20 | box-sizing: content-box;
21 | `
22 |
23 | const Moon = styled(`div`)`
24 | position: absolute;
25 | top: ${({ size, sizeUnit }) => `${size / 2 - moonSize(size) / 2}${sizeUnit}`};
26 | background-color: ${({ color }) => color};
27 | opacity: 0.8;
28 | animation: ${moon} 0.6s linear 0s infinite normal forwards running;
29 | box-sizing: content-box;
30 | ${({ size, sizeUnit }) => ballStyle(moonSize(size), sizeUnit)};
31 | `
32 |
33 | const Ring = styled(`div`)`
34 | border-width: ${({ size }) => `${moonSize(size)}px`};
35 | border-style: solid;
36 | border-color: ${({ color }) => `${color}`};
37 | border-image: initial;
38 | opacity: 0.1;
39 | box-sizing: content-box;
40 | ${({ size, sizeUnit }) => ballStyle(size, sizeUnit)};
41 | `
42 |
43 | export const MoonLoader = {
44 | functional: true,
45 | props: {
46 | loading: { type: Boolean, default: true },
47 | color: { type: String, default: `#000000` },
48 | size: { type: Number, default: 60 },
49 | sizeUnit: { type: String, default: `px` }
50 | },
51 | render(h, { props, data }) {
52 | return props.loading ? (
53 |
58 |
59 |
64 |
65 | ) : null
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/components/pacmanLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | // This returns an animation
5 | const pacman = [
6 | keyframes`
7 | 0% {transform: rotate(0deg)}
8 | 50% {transform: rotate(-44deg)}
9 | `,
10 | keyframes`
11 | 0% {transform: rotate(0deg)}
12 | 50% {transform: rotate(44deg)}
13 | `
14 | ]
15 |
16 | const Wrapper = styled(`div`)`
17 | position: relative;
18 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
19 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
20 | font-size: 0;
21 | `
22 |
23 | const s1 = (size, sizeUnit) => `${size}${sizeUnit} solid transparent`
24 | const s2 = (size, sizeUnit, color) => `${size}${sizeUnit} solid ${color}`
25 |
26 | const Pacman = styled(`div`)`
27 | position: absolute;
28 | width: 0;
29 | height: 0;
30 | border-top: ${({ size, sizeUnit, color, version }) => (version === 0 ? s1(size, sizeUnit) : s2(size, sizeUnit, color))};
31 | border-left: ${({ size, sizeUnit, color }) => s2(size, sizeUnit, color)};
32 | border-bottom: ${({ size, sizeUnit, color, version }) => (version === 0 ? s2(size, sizeUnit, color) : s1(size, sizeUnit))};
33 | border-right: ${({ size, sizeUnit }) => s1(size, sizeUnit)};
34 | border-radius: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
35 | animation: ${({ version }) => pacman[version]} ease-in-out 0.8s infinite normal both running;
36 | `
37 |
38 | const ballAnim = (size, sizeUnit) => keyframes`
39 | 75% {opacity: 0.7}
40 | 100% {transform: translate(${-4 * size}${sizeUnit}, ${-size / 4}${sizeUnit})}
41 | `
42 |
43 | const Ball = styled(`div`)`
44 | position: absolute;
45 | top: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
46 | left: ${({ size, sizeUnit }) => `${size * 4}${sizeUnit}`};
47 | width: ${({ size, sizeUnit }) => `${size / 2.5}${sizeUnit}`};
48 | height: ${({ size, sizeUnit }) => `${size / 2.5}${sizeUnit}`};
49 | margin: ${({ margin }) => margin};
50 | border-radius: 100%;
51 | background-color: ${({ color }) => color};
52 | transform: ${({ size, sizeUnit }) => `translate(0, ${-size / 4}${sizeUnit})`};
53 | animation: ${({ size, sizeUnit, version }) => `${ballAnim(size, sizeUnit)} 1s linear ${version * 0.25}s infinite normal both running`};
54 | `
55 |
56 | export const PacmanLoader = {
57 | functional: true,
58 | props: {
59 | loading: { type: Boolean, default: true },
60 | color: { type: String, default: `#000000` },
61 | size: { type: Number, default: 25 },
62 | sizeUnit: { type: String, default: `px` },
63 | margin: { type: String, default: `2px` }
64 | },
65 | render(h, { props, data }) {
66 | return props.loading ? (
67 |
72 |
78 |
84 | {range(4, 2).map(i => (
85 |
92 | ))}
93 |
94 | ) : null
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/components/propagateLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | // 1.5 4.5 7.5
5 | const distance = [1, 3, 5]
6 |
7 | const propagate = [
8 | keyframes`
9 | 25% {transform: translateX(-${distance[0]}rem) scale(0.75)}
10 | 50% {transform: translateX(-${distance[1]}rem) scale(0.6)}
11 | 75% {transform: translateX(-${distance[2]}rem) scale(0.5)}
12 | 95% {transform: translateX(0rem) scale(1)}
13 | `,
14 | keyframes`
15 | 25% {transform: translateX(-${distance[0]}rem) scale(0.75)}
16 | 50% {transform: translateX(-${distance[1]}rem) scale(0.6)}
17 | 75% {transform: translateX(-${distance[1]}rem) scale(0.6)}
18 | 95% {transform: translateX(0rem) scale(1)}
19 | `,
20 | keyframes`
21 | 25% {transform: translateX(-${distance[0]}rem) scale(0.75)}
22 | 75% {transform: translateX(-${distance[0]}rem) scale(0.75)}
23 | 95% {transform: translateX(0rem) scale(1)}
24 | `,
25 | keyframes`
26 | 25% {transform: translateX(${distance[0]}rem) scale(0.75)}
27 | 75% {transform: translateX(${distance[0]}rem) scale(0.75)}
28 | 95% {transform: translateX(0rem) scale(1)}
29 | `,
30 | keyframes`
31 | 25% {transform: translateX(${distance[0]}rem) scale(0.75)}
32 | 50% {transform: translateX(${distance[1]}rem) scale(0.6)}
33 | 75% {transform: translateX(${distance[1]}rem) scale(0.6)}
34 | 95% {transform: translateX(0rem) scale(1)}
35 | `,
36 | keyframes`
37 | 25% {transform: translateX(${distance[0]}rem) scale(0.75)}
38 | 50% {transform: translateX(${distance[1]}rem) scale(0.6)}
39 | 75% {transform: translateX(${distance[2]}rem) scale(0.5)}
40 | 95% {transform: translateX(0rem) scale(1)}
41 | `
42 | ]
43 |
44 | const Wrapper = styled(`div`)`
45 | position: relative;
46 | `
47 |
48 | const Circle = styled(`div`)`
49 | position: absolute;
50 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
51 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
52 | border-radius: 50%;
53 | background: ${({ color }) => color};
54 | font-size: ${({ size, sizeUnit }) => `${size / 3}${sizeUnit}`};
55 | animation: ${({ version }) => propagate[version]} 1.5s infinite;
56 | animation-fill-mode: forwards;
57 | `
58 |
59 | export const PropagateLoader = {
60 | functional: true,
61 | props: {
62 | loading: { type: Boolean, default: true },
63 | color: { type: String, default: `#000000` },
64 | size: { type: Number, default: 15 },
65 | sizeUnit: { type: String, default: `px` }
66 | },
67 | render(h, { props, data }) {
68 | return props.loading ? (
69 |
70 | {range(6).map(i => (
71 |
77 | ))}
78 |
79 | ) : null
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/components/pulseLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const pulse = keyframes`
5 | 0% {transform: scale(1);opacity: 1}
6 | 45% {transform: scale(0.1);opacity: 0.7}
7 | 80% {transform: scale(1);opacity: 1}
8 | `
9 |
10 | const Circle = styled(`div`)`
11 | display: inline-block;
12 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
13 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
14 | margin: ${({ margin }) => margin};
15 | border-radius: 100%;
16 | background-color: ${({ color }) => color};
17 | animation: ${({ version }) => `${pulse} 0.75s ${version * 0.12}s infinite cubic-bezier(0.2, 0.68, 0.18, 1.08)`};
18 | animation-fill-mode: both;
19 | `
20 |
21 | export const PulseLoader = {
22 | functional: true,
23 | props: {
24 | loading: { type: Boolean, default: true },
25 | color: { type: String, default: `#000000` },
26 | size: { type: Number, default: 15 },
27 | sizeUnit: { type: String, default: `px` },
28 | margin: { type: String, default: `2px` }
29 | },
30 | render(h, { props, data }) {
31 | return props.loading ? (
32 |
33 | {range(3, 1).map(i => (
34 |
41 | ))}
42 |
43 | ) : null
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/ringLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const right = keyframes`
5 | 0% {transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg)}
6 | 100% {transform: rotateX(180deg) rotateY(360deg) rotateZ(360deg)}
7 | `
8 |
9 | const left = keyframes`
10 | 0% {transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg)}
11 | 100% {transform: rotateX(360deg) rotateY(180deg) rotateZ(360deg)}
12 | `
13 |
14 | const Wrapper = styled(`div`)`
15 | position: relative;
16 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
17 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
18 | `
19 |
20 | const Ring = styled(`div`)`
21 | position: absolute;
22 | top: 0;
23 | left: 0;
24 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
25 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
26 | border: ${({ size, sizeUnit, color }) => `${size / 10}${sizeUnit} solid ${color}`};
27 | border-radius: 100%;
28 | opacity: 0.4;
29 | animation: ${({ version }) => `${(version === 1 ? right : left)} 2s 0s infinite linear`};
30 | animation-fill-mode: forwards;
31 | perspective: 800px;
32 | `
33 |
34 | export const RingLoader = {
35 | functional: true,
36 | props: {
37 | loading: { type: Boolean, default: true },
38 | color: { type: String, default: `#000000` },
39 | size: { type: Number, default: 60 },
40 | sizeUnit: { type: String, default: `px` }
41 | },
42 | render(h, { props, data }) {
43 | return props.loading ? (
44 |
49 | {range(2, 1).map(i => (
50 |
56 | ))}
57 |
58 | ) : null
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/riseLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const riseAmount = 30
5 |
6 | const even = keyframes`
7 | 0% {transform: scale(1.1)}
8 | 25% {translateY(-${riseAmount}px)}
9 | 50% {transform: scale(0.4)}
10 | 75% {transform: translateY(${riseAmount}px)}
11 | 100% {transform: translateY(0) scale(1.0)}
12 | `
13 |
14 | const odd = keyframes`
15 | 0% {transform: scale(0.4)}
16 | 25% {translateY(${riseAmount}px)}
17 | 50% {transform: scale(1.1)}
18 | 75% {transform: translateY(${-riseAmount}px)}
19 | 100% {transform: translateY(0) scale(0.75)}
20 | `
21 |
22 | const Circle = styled(`div`)`
23 | display: inline-block;
24 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
25 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
26 | margin: ${({ margin }) => margin};
27 | border-radius: 100%;
28 | background-color: ${({ color }) => color};
29 | animation: ${({ version }) => `${(version % 2 === 0 ? even : odd)} 1s 0s infinite cubic-bezier(0.15, 0.46, 0.9, 0.6)`};
30 | animation-fill-mode: both;
31 | `
32 |
33 | export const RiseLoader = {
34 | functional: true,
35 | props: {
36 | loading: { type: Boolean, default: true },
37 | color: { type: String, default: `#000000` },
38 | size: { type: Number, default: 15 },
39 | sizeUnit: { type: String, default: `px` },
40 | margin: { type: String, default: `2px` }
41 | },
42 | render(h, { props, data }) {
43 | return props.loading ? (
44 |
45 | {range(5, 1).map(i => (
46 |
53 | ))}
54 |
55 | ) : null
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/rotateLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { css, keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const rotate = keyframes`
5 | 0% {transform: rotate(0deg)}
6 | 50% {transform: rotate(180deg)}
7 | 100% {transform: rotate(360deg)}
8 | `
9 |
10 | const fill = (color, margin, size, sizeUnit) => css`
11 | width: ${`${size}${sizeUnit}`};
12 | height: ${`${size}${sizeUnit}`};
13 | margin: ${margin};
14 | border-radius: 100%;
15 | background-color: ${color};
16 | `
17 |
18 | const Wrapper = styled(`div`)`
19 | position: relative;
20 | display: inline-block;
21 | animation: ${rotate} 1s 0s infinite cubic-bezier(0.7, -0.13, 0.22, 0.86);
22 | animation-fill-mode: both;
23 | ${({ color, margin, size, sizeUnit }) => fill(color, margin, size, sizeUnit)}
24 | `
25 |
26 | const Circle = styled(`div`)`
27 | position: absolute;
28 | top: 0;
29 | left: ${({ side }) => `${side ? -28 : 25}px`};
30 | opacity: 0.8;
31 | ${({ color, margin, size, sizeUnit }) => `${fill(color, margin, size, sizeUnit)}`}
32 | `
33 |
34 | export const RotateLoader = {
35 | functional: true,
36 | props: {
37 | loading: { type: Boolean, default: true },
38 | color: { type: String, default: `#000000` },
39 | size: { type: Number, default: 15 },
40 | sizeUnit: { type: String, default: `px` },
41 | margin: { type: String, default: `2px` }
42 | },
43 | render(h, { props, data }) {
44 | return props.loading ? (
45 |
52 | {range(2).map(i => (
53 |
60 | ))}
61 |
62 | ) : null
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/scaleLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const scale = keyframes`
5 | 0% {transform: scaley(1.0)}
6 | 50% {transform: scaley(0.4)}
7 | 100% {transform: scaley(1.0)}
8 | `
9 |
10 | const Bar = styled(`div`)`
11 | display: inline-block;
12 | width: ${({ width, widthUnit }) => `${width}${widthUnit}`};
13 | height: ${({ height, heightUnit }) => `${height}${heightUnit}`};
14 | margin: ${({ margin }) => margin};
15 | border-radius: ${({ radius, radiusUnit }) => `${radius}${radiusUnit}`};
16 | background-color: ${({ color }) => color};
17 | animation: ${({ version }) => `${scale} 1s cubic-bezier(0.2, 0.68, 0.18, 1.08) ${version * 0.1}s infinite normal both running`};
18 | `
19 |
20 | export const ScaleLoader = {
21 | functional: true,
22 | props: {
23 | loading: { type: Boolean, default: true },
24 | color: { type: String, default: `#000000` },
25 | height: { type: Number, default: 35 },
26 | heightUnit: { type: String, default: `px` },
27 | width: { type: Number, default: 4 },
28 | widthUnit: { type: String, default: `px` },
29 | radius: { type: Number, default: 2 },
30 | radiusUnit: { type: String, default: `px` },
31 | margin: { type: String, default: `2px` }
32 | },
33 | render(h, { props, data }) {
34 | return props.loading ? (
35 |
36 | {range(5, 1).map(i => (
37 |
48 | ))}
49 |
50 | ) : null
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/skewLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 |
3 | const skew = keyframes`
4 | 25% {transform: perspective(100px) rotateX(180deg) rotateY(0)}
5 | 50% {transform: perspective(100px) rotateX(180deg) rotateY(180deg)}
6 | 75% {transform: perspective(100px) rotateX(0) rotateY(180deg)}
7 | 100% {transform: perspective(100px) rotateX(0) rotateY(0)}
8 | `
9 |
10 | const Triangle = styled(`div`)`
11 | display: inline-block;
12 | width: 0;
13 | height: 0;
14 | border-left: ${({ size, sizeUnit }) => `${size}${sizeUnit} solid transparent`};
15 | border-right: ${({ size, sizeUnit }) => `${size}${sizeUnit} solid transparent`};
16 | border-bottom: ${({ size, sizeUnit, color }) => `${size}${sizeUnit} solid ${color}`};
17 | animation: ${skew} 3s 0s infinite cubic-bezier(.09,.57,.49,.9);
18 | animation-fill-mode: both;
19 | `
20 |
21 | export const SkewLoader = {
22 | functional: true,
23 | props: {
24 | loading: { type: Boolean, default: true },
25 | color: { type: String, default: `#000000` },
26 | size: { type: Number, default: 20 },
27 | sizeUnit: { type: String, default: `px` }
28 | },
29 | render(h, { props, data }) {
30 | return props.loading ? (
31 |
37 | ) : null
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/squareLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 |
3 | const square = keyframes`
4 | 25% {transform: rotateX(180deg) rotateY(0)}
5 | 50% {transform: rotateX(180deg) rotateY(180deg)}
6 | 75% {transform: rotateX(0) rotateY(180deg)}
7 | 100% {transform: rotateX(0) rotateY(0)}
8 | `
9 |
10 | const Square = styled(`div`)`
11 | display: inline-block;
12 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
13 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
14 | background-color: ${({ color }) => color};
15 | animation: ${square} 3s 0s infinite cubic-bezier(0.09, 0.57, 0.49, 0.9);
16 | animation-fill-mode: both;
17 | `
18 |
19 | export const SquareLoader = {
20 | functional: true,
21 | props: {
22 | loading: { type: Boolean, default: true },
23 | color: { type: String, default: `#000000` },
24 | size: { type: Number, default: 50 },
25 | sizeUnit: { type: String, default: `px` }
26 | },
27 | render(h, { props, data }) {
28 | return props.loading ? (
29 |
35 | ) : null
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/syncLoader.js:
--------------------------------------------------------------------------------
1 | import styled, { keyframes } from 'vue-emotion'
2 | import { range } from '../utils'
3 |
4 | const sync = keyframes`
5 | 33% {transform: translateY(10px)}
6 | 66% {transform: translateY(-10px)}
7 | 100% {transform: translateY(0)}
8 | `
9 |
10 | const Circle = styled(`div`)`
11 | display: inline-block;
12 | width: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
13 | height: ${({ size, sizeUnit }) => `${size}${sizeUnit}`};
14 | margin: ${({ margin }) => margin};
15 | border-radius: 100%;
16 | background-color: ${({ color }) => color};
17 | animation: ${({ version }) => `${sync} 0.6s ease-in-out ${version * 0.07}s infinite normal both running`};
18 | box-sizing: content-box;
19 | `
20 |
21 | export const SyncLoader = {
22 | functional: true,
23 | props: {
24 | loading: { type: Boolean, default: true },
25 | color: { type: String, default: `#000000` },
26 | size: { type: Number, default: 15 },
27 | sizeUnit: { type: String, default: `px` },
28 | margin: { type: String, default: `2px` }
29 | },
30 | render(h, { props, data }) {
31 | return props.loading ? (
32 |
33 | {range(3, 1).map(i => (
34 |
41 | ))}
42 |
43 | ) : null
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as Components from './components'
2 |
3 | export function install(Vue) {
4 | if (install.installed) return
5 | install.installed = true
6 | Object.entries(Components).forEach(([name, component]) => {
7 | Vue.component(name, component)
8 | })
9 | }
10 |
11 | export const VueSpinners = {
12 | install
13 | }
14 |
15 | let GlobalVue = null
16 | if (typeof window !== `undefined`) {
17 | GlobalVue = window.Vue
18 | } else if (typeof global !== `undefined`) {
19 | GlobalVue = global.Vue
20 | }
21 | if (GlobalVue) {
22 | GlobalVue.use(VueSpinners)
23 | }
24 |
25 | export default Components
26 | export * from './components'
27 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export const calculateRgba = (input, opacity) => {
2 | let color
3 | if (input[0] === `#`) {
4 | color = input.slice(1)
5 | }
6 |
7 | if (color.length === 3) {
8 | let res = ``
9 | color.split(``).forEach(c => {
10 | res += c
11 | res += c
12 | })
13 | color = res
14 | }
15 |
16 | const rgbValues = color
17 | .match(/.{2}/g)
18 | .map(hex => parseInt(hex, 16))
19 | .join(`, `)
20 | return `rgba(${rgbValues}, ${opacity})`
21 | }
22 |
23 | export const range = (size, startAt = 0) =>
24 | [...Array(size).keys()].map(i => i + startAt)
25 |
26 | export const characterRange = (startChar, endChar) =>
27 | String.fromCharCode(
28 | ...range(
29 | endChar.charCodeAt(0) - startChar.charCodeAt(0),
30 | startChar.charCodeAt(0)
31 | )
32 | )
33 |
34 | export const zip = (arr, ...arrs) =>
35 | arr.map((val, i) => arrs.reduce((list, curr) => [...list, curr[i]], [val]))
36 |
--------------------------------------------------------------------------------