|
@@ -1,62 +1,52 @@
|
|
|
import {keyframes} from '@emotion/react';
|
|
|
import styled from '@emotion/styled';
|
|
|
|
|
|
-export function TimelinePlaceholder({count}: {count: number}) {
|
|
|
+import {space} from 'sentry/styles/space';
|
|
|
+
|
|
|
+const SPACING = 15;
|
|
|
+
|
|
|
+export function TimelinePlaceholder() {
|
|
|
return (
|
|
|
- <TimelinePlaceholderContainer>
|
|
|
- {new Array(count).fill(null).map((_, i) => (
|
|
|
- <PlaceholderTick
|
|
|
- key={i}
|
|
|
- style={{
|
|
|
- left: `${(i * (100 / count)).toFixed(2)}%`,
|
|
|
- animationDelay: `${(i / count).toFixed(2)}s`,
|
|
|
- }}
|
|
|
- />
|
|
|
- ))}
|
|
|
- </TimelinePlaceholderContainer>
|
|
|
+ <PlaceholderSvg xmlns="http://www.w3.org/2000/svg" width="100%" height="14px">
|
|
|
+ <defs>
|
|
|
+ <pattern
|
|
|
+ id="tick-pattern"
|
|
|
+ patternUnits="userSpaceOnUse"
|
|
|
+ width={SPACING + 4}
|
|
|
+ height={24}
|
|
|
+ >
|
|
|
+ <rect width="4px" height="14px" rx="2" fill="white" />
|
|
|
+ </pattern>
|
|
|
+ <mask id="pattern-mask">
|
|
|
+ <rect width="100%" height="100%" fill="url(#tick-pattern)" />
|
|
|
+ </mask>
|
|
|
+ </defs>
|
|
|
+ <foreignObject width="100%" height="14" mask="url(#pattern-mask)">
|
|
|
+ <AnimatedGradient />
|
|
|
+ </foreignObject>
|
|
|
+ </PlaceholderSvg>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
-const TimelinePlaceholderContainer = styled('div')`
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- position: relative;
|
|
|
- height: 100%;
|
|
|
+const PlaceholderSvg = styled('svg')`
|
|
|
+ margin: ${space(0.5)} 0;
|
|
|
`;
|
|
|
|
|
|
-const placeholderTickKeyframes = keyframes`
|
|
|
- 0% {
|
|
|
- opacity: 0;
|
|
|
- transform: scale(0.75) translateZ(0);
|
|
|
- filter: blur(12px);
|
|
|
- }
|
|
|
- 33.33% {
|
|
|
- opacity: 1;
|
|
|
- transform: scale(1) translateZ(0);
|
|
|
- filter: blur(0px);
|
|
|
- }
|
|
|
- 66.66% {
|
|
|
- opacity: 1;
|
|
|
- transform: scale(1) translateZ(0);
|
|
|
- filter: blur(0px);
|
|
|
- }
|
|
|
- 100% {
|
|
|
- opacity: 0;
|
|
|
- transform: scale(0.75) translateZ(0);
|
|
|
- filter: blur(12px);
|
|
|
- }
|
|
|
+const gradientAnimation = keyframes`
|
|
|
+ 0%{ background-position-x: 0%; }
|
|
|
+ 100%{ background-position-x: -200%; }
|
|
|
`;
|
|
|
|
|
|
-const PlaceholderTick = styled('div')`
|
|
|
- position: absolute;
|
|
|
- margin-top: 1px;
|
|
|
- background: ${p => p.theme.translucentBorder};
|
|
|
- width: 4px;
|
|
|
- height: 14px;
|
|
|
- border-radius: 2px;
|
|
|
-
|
|
|
- opacity: 0;
|
|
|
- transform: scale(0.75) translateZ(0);
|
|
|
- filter: blur(12px);
|
|
|
- animation: ${placeholderTickKeyframes} 2s ease-out forwards infinite;
|
|
|
+const AnimatedGradient = styled('div')`
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background-image: linear-gradient(
|
|
|
+ 90deg,
|
|
|
+ ${p => p.theme.border} 0%,
|
|
|
+ rgba(255, 255, 255, 0) 20%,
|
|
|
+ rgba(255, 255, 255, 0) 80%,
|
|
|
+ ${p => p.theme.border} 100%
|
|
|
+ );
|
|
|
+ background-size: 200% 100%;
|
|
|
+ animation: ${gradientAnimation} 2s linear infinite;
|
|
|
`;
|