|
1 | 1 | import type { FunctionalComponent, VNode } from 'vue'
|
2 | 2 | import { h } from 'vue'
|
3 | 3 |
|
| 4 | +const getLength = (size: number | string): string => |
| 5 | + typeof size === 'number' ? `${size}px` : size |
| 6 | + |
4 | 7 | /**
|
5 | 8 | * Loading icon
|
6 | 9 | */
|
7 | 10 | export const LoadingIcon: FunctionalComponent<{
|
8 | 11 | size?: number
|
9 | 12 | stroke?: number
|
10 | 13 | wrapper?: boolean
|
11 |
| - height?: number |
| 14 | + height?: number | string |
12 | 15 | }> = ({ size = 48, stroke = 4, wrapper = true, height = 2 * size }): VNode => {
|
13 |
| - const icon = h( |
14 |
| - 'svg', |
15 |
| - { |
16 |
| - xmlns: 'http://www.w3.org/2000/svg', |
17 |
| - width: size, |
18 |
| - height: size, |
19 |
| - preserveAspectRatio: 'xMidYMid', |
20 |
| - viewBox: '25 25 50 50', |
21 |
| - }, |
22 |
| - [ |
23 |
| - h('animateTransform', { |
24 |
| - attributeName: 'transform', |
25 |
| - type: 'rotate', |
26 |
| - dur: '2s', |
27 |
| - keyTimes: '0;1', |
28 |
| - repeatCount: 'indefinite', |
29 |
| - values: '0;360', |
30 |
| - }), |
31 |
| - h( |
32 |
| - 'circle', |
33 |
| - { |
34 |
| - 'cx': '50', |
35 |
| - 'cy': '50', |
36 |
| - 'r': '20', |
37 |
| - 'fill': 'none', |
38 |
| - 'stroke': 'currentColor', |
39 |
| - 'stroke-width': stroke, |
40 |
| - 'stroke-linecap': 'round', |
41 |
| - }, |
42 |
| - [ |
43 |
| - h('animate', { |
44 |
| - attributeName: 'stroke-dasharray', |
45 |
| - dur: '1.5s', |
46 |
| - keyTimes: '0;0.5;1', |
47 |
| - repeatCount: 'indefinite', |
48 |
| - values: '1,200;90,200;1,200', |
49 |
| - }), |
50 |
| - h('animate', { |
51 |
| - attributeName: 'stroke-dashoffset', |
52 |
| - dur: '1.5s', |
53 |
| - keyTimes: '0;0.5;1', |
54 |
| - repeatCount: 'indefinite', |
55 |
| - values: '0;-35px;-125px', |
56 |
| - }), |
57 |
| - ], |
58 |
| - ), |
59 |
| - ], |
60 |
| - ) |
| 16 | + const icon = h('span', { |
| 17 | + style: `\ |
| 18 | +--loading-icon: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='xMidYMid' viewBox='25 25 50 50'%3E%3CanimateTransform attributeName='transform' type='rotate' dur='2s' keyTimes='0;1' repeatCount='indefinite' values='0;360'%3E%3C/animateTransform%3E%3Ccircle cx='50' cy='50' r='20' fill='none' stroke='currentColor' stroke-width='${stroke}' stroke-linecap='round'%3E%3Canimate attributeName='stroke-dasharray' dur='1.5s' keyTimes='0;0.5;1' repeatCount='indefinite' values='1,200;90,200;1,200'%3E%3C/animate%3E%3Canimate attributeName='stroke-dashoffset' dur='1.5s' keyTimes='0;0.5;1' repeatCount='indefinite' values='0;-35px;-125px'%3E%3C/animate%3E%3C/circle%3E%3C/svg%3E"); |
| 19 | +--icon-size: ${getLength(size)}; |
| 20 | +display: inline-block; |
| 21 | +width: var(--icon-size); |
| 22 | +height: var(--icon-size); |
| 23 | +background-color: currentcolor; |
| 24 | +-webkit-mask-image: var(--loading-icon); |
| 25 | +mask-image: var(--loading-icon); |
| 26 | +`, |
| 27 | + }) |
61 | 28 |
|
62 | 29 | return wrapper
|
63 | 30 | ? h(
|
64 | 31 | 'div',
|
65 | 32 | {
|
66 |
| - class: 'loading-icon-wrapper', |
67 |
| - style: `display:flex;align-items:center;justify-content:center;height:${height}px`, |
| 33 | + style: `\ |
| 34 | +display: flex; |
| 35 | +align-items: center; |
| 36 | +justify-content: center; |
| 37 | +height: ${getLength(height)}`, |
68 | 38 | },
|
69 | 39 | icon,
|
70 | 40 | )
|
|
0 commit comments