Skip to content

Commit 84d599f

Browse files
committed
feat(ai-assist): animated loading state (#13841)
1 parent c16e5be commit 84d599f

File tree

6 files changed

+158
-16
lines changed

6 files changed

+158
-16
lines changed
Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,73 @@
11
@use "scss/variables";
2+
@use "scss/mixins";
3+
@use "scss/colors";
24

35
.container {
46
margin: 0 variables.$spacing-2xl;
57
}
68

7-
.aiProcessingImage {
9+
.assistLoadingAnimation {
810
width: 100%;
9-
height: auto;
10-
margin-bottom: variables.$spacing-md;
11+
max-width: variables.$width-modal-md;
12+
13+
// Alternate offset of the skeletons
14+
> * {
15+
&:nth-child(even) {
16+
margin-left: variables.$spacing-2xl;
17+
}
18+
19+
&:nth-child(odd) {
20+
margin-right: variables.$spacing-2xl;
21+
}
22+
}
23+
24+
> div {
25+
.loadingSkeleton {
26+
height: variables.$button-height-lg;
27+
border-radius: variables.$border-radius-xs;
28+
29+
// Add a hover animation to the skeleton
30+
transition: transform 0.3s ease-in-out;
31+
32+
&:hover {
33+
transform: scale(1.05);
34+
}
35+
}
36+
37+
// Offset the animation of the skeleton
38+
&:nth-child(even) {
39+
.loadingSkeleton {
40+
animation-delay: -2s;
41+
}
42+
}
43+
44+
&:nth-child(3n) {
45+
.loadingSkeleton {
46+
animation-delay: -4.5s;
47+
}
48+
}
49+
}
50+
}
51+
52+
.floatingAnimation {
53+
animation: float-side-to-side 8s ease-in-out infinite;
54+
55+
&:nth-child(even) {
56+
animation-delay: -2.5s;
57+
}
58+
59+
&:nth-child(3n) {
60+
animation-delay: -5s;
61+
}
62+
}
63+
64+
@keyframes float-side-to-side {
65+
0%,
66+
100% {
67+
transform: translateX(0);
68+
}
69+
70+
50% {
71+
transform: translateX(10px);
72+
}
1173
}

airbyte-webapp/src/components/connectorBuilder/Builder/Assist/AssistWaiting.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,35 @@ import { FormattedMessage } from "react-intl";
33

44
import { Button } from "components/ui/Button";
55
import { FlexContainer } from "components/ui/Flex";
6+
import { LoadingSkeleton } from "components/ui/LoadingSkeleton";
67
import { Text } from "components/ui/Text";
78

8-
import aiProcessing from "./assistwaiting.gif";
99
import styles from "./AssistWaiting.module.scss";
1010

1111
interface AssistWaitingProps {
1212
onSkip: () => void;
1313
}
1414

15+
const FloatingLoadingSkeleton = () => (
16+
<div className={styles.floatingAnimation}>
17+
<LoadingSkeleton variant="magic" className={styles.loadingSkeleton} />
18+
</div>
19+
);
20+
21+
const AssistLoadingAnimation = () => {
22+
return (
23+
<FlexContainer direction="column" gap="lg" className={styles.assistLoadingAnimation}>
24+
<FloatingLoadingSkeleton />
25+
<FloatingLoadingSkeleton />
26+
<FloatingLoadingSkeleton />
27+
</FlexContainer>
28+
);
29+
};
30+
1531
export const AssistWaiting: React.FC<AssistWaitingProps> = ({ onSkip }) => {
1632
return (
1733
<FlexContainer alignItems="center" direction="column" gap="md" className={styles.container}>
18-
<img src={aiProcessing} alt="" className={styles.aiProcessingImage} />
34+
<AssistLoadingAnimation />
1935
<Text size="xl" color="darkBlue">
2036
<FormattedMessage id="connectorBuilder.assist.waiting.message" />
2137
</Text>
Binary file not shown.
Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,52 @@
11
@use "scss/colors";
22
@use "scss/variables";
3+
@use "scss/mixins";
34

45
.loadingSkeleton {
6+
background: colors.$loading-skeleton-base;
7+
8+
&--buttonHeight {
9+
height: variables.$button-height-xs;
10+
}
11+
}
12+
13+
.loadingSkeletonShimmer {
514
animation-duration: 1.6s;
615
animation-fill-mode: forwards;
716
animation-iteration-count: infinite;
817
animation-name: shimmer;
918
animation-timing-function: linear;
10-
background: colors.$loading-skeleton-base;
1119
background: linear-gradient(
1220
to right,
1321
colors.$loading-skeleton-stop-1 8%,
1422
colors.$loading-skeleton-stop-2 25%,
1523
colors.$loading-skeleton-stop-1 50%
1624
);
1725
background-size: 1200px 100%;
26+
}
1827

19-
&--buttonHeight {
20-
height: variables.$button-height-xs;
21-
}
28+
.loadingSkeletonMagic {
29+
@include mixins.gradient-border(
30+
variables.$border-thin,
31+
linear-gradient(
32+
var(--gradient-angle),
33+
colors.$grey-100 0%,
34+
colors.$grey-100 70%,
35+
colors.$gradient-primary-start 70%,
36+
colors.$gradient-primary-stop 100%
37+
)
38+
);
39+
40+
border: variables.$border-thin solid transparent;
41+
animation-duration: 7s;
42+
animation-iteration-count: infinite;
43+
animation-name: rotate-gradient;
44+
animation-timing-function: linear;
45+
animation-delay: 0s;
2246
}
2347

48+
// ANIMATIONS
49+
2450
@keyframes shimmer {
2551
0% {
2652
background-position: -1200px 0;
@@ -30,3 +56,19 @@
3056
background-position: 1200px 0;
3157
}
3258
}
59+
60+
@property --gradient-angle {
61+
syntax: "<angle>";
62+
initial-value: 0deg;
63+
inherits: false;
64+
}
65+
66+
@keyframes rotate-gradient {
67+
0% {
68+
--gradient-angle: 0deg;
69+
}
70+
71+
100% {
72+
--gradient-angle: 360deg;
73+
}
74+
}

airbyte-webapp/src/components/ui/LoadingSkeleton/LoadingSkeleton.stories.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,20 @@ export default {
1111
} as StoryObj<typeof LoadingSkeleton>;
1212

1313
export const Default: StoryObj<typeof LoadingSkeleton> = {
14-
args: {},
15-
render: () => (
14+
args: {
15+
variant: "shimmer",
16+
},
17+
argTypes: {
18+
variant: {
19+
control: { type: "radio" },
20+
options: ["shimmer", "magic"],
21+
},
22+
},
23+
render: ({ variant }) => (
1624
<FlexContainer direction="column" gap="xl">
17-
<LoadingSkeleton />
18-
<LoadingSkeleton />
19-
<LoadingSkeleton />
25+
<LoadingSkeleton variant={variant} />
26+
<LoadingSkeleton variant={variant} />
27+
<LoadingSkeleton variant={variant} />
2028
</FlexContainer>
2129
),
2230
};

airbyte-webapp/src/components/ui/LoadingSkeleton/LoadingSkeleton.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@ import classNames from "classnames";
22

33
import styles from "./LoadingSkeleton.module.scss";
44

5-
export const LoadingSkeleton = ({ className }: { className?: string }) => {
6-
return <div className={classNames(styles.loadingSkeleton, styles["loadingSkeleton--buttonHeight"], className)} />;
5+
export type LoadingSkeletonVariants = "shimmer" | "magic";
6+
7+
export interface LoadingSkeletonProps {
8+
className?: string;
9+
variant?: LoadingSkeletonVariants;
10+
}
11+
12+
export const LoadingSkeleton = ({ className, variant = "shimmer" }: LoadingSkeletonProps) => {
13+
const variantStyles = {
14+
[styles.loadingSkeletonShimmer]: variant === "shimmer",
15+
[styles.loadingSkeletonMagic]: variant === "magic",
16+
};
17+
18+
const classes = classNames(styles.loadingSkeleton, styles["loadingSkeleton--buttonHeight"], variantStyles, className);
19+
20+
return <div className={classes} />;
721
};

0 commit comments

Comments
 (0)