Add loading view component
This commit is contained in:
parent
f3a73c4d20
commit
f58d451432
6
public/icons/rotate-clockwise.svg
Normal file
6
public/icons/rotate-clockwise.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-rotate-clockwise" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M4.05 11a8 8 0 1 1 .5 4m-.5 5v-5h5" />
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 349 B |
@ -17,6 +17,7 @@
|
||||
--gap-large: 32px;
|
||||
|
||||
--duration-short: 0.3s;
|
||||
--duration-long: 1.5s;
|
||||
}
|
||||
|
||||
.grow {
|
||||
@ -208,6 +209,31 @@ i {
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading */
|
||||
|
||||
.loading-icon {
|
||||
animation: spin infinite linear var(--duration-long),
|
||||
fadein linear var(--duration-short);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Icon buttons */
|
||||
|
||||
.icon-button {
|
||||
@ -306,6 +332,14 @@ i {
|
||||
|
||||
/* Expression set page */
|
||||
|
||||
.page-loading {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.page-with-padding {
|
||||
padding: var(--gap-small) var(--gap-medium) var(--gap-medium);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { useAllExpressions } from "../../hooks";
|
||||
import { IndexedExpression } from "../../model";
|
||||
import { AppPath, AppRouting } from "../../model/routing";
|
||||
import { ErrorView } from "../ErrorView";
|
||||
import { LoadingView } from "../LoadingView/LoadingView";
|
||||
|
||||
export function ExpressionCardListView() {
|
||||
const { setRoute } = useContext(AppRouting);
|
||||
@ -13,7 +14,7 @@ export function ExpressionCardListView() {
|
||||
[expressions]
|
||||
);
|
||||
|
||||
if (!expressions) return null; // LOADING
|
||||
if (!expressions) return <LoadingView />;
|
||||
if (!expressions.length) {
|
||||
return <ErrorView message="No expression cards yet" />;
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ import { ExpressionDescription } from "../../components/ExpressionDescription";
|
||||
import { useExpressionById, useQueryExpressionId } from "../../hooks";
|
||||
import { ItemNotFoundError } from "../../model";
|
||||
import { ErrorView } from "../ErrorView";
|
||||
import { LoadingView } from "../LoadingView/LoadingView";
|
||||
|
||||
export function ExpressionCardView() {
|
||||
const expression_id = useQueryExpressionId();
|
||||
const expression = useExpressionById(expression_id);
|
||||
|
||||
if (expression === undefined) return null; // LOADING
|
||||
if (expression === undefined) return <LoadingView />;
|
||||
if (expression === ItemNotFoundError)
|
||||
return <ErrorView message="Expression card not found" />;
|
||||
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
import { IndexedExpression } from "../../model";
|
||||
import { sample } from "../../util";
|
||||
import { ErrorView } from "../ErrorView";
|
||||
import { LoadingView } from "../LoadingView/LoadingView";
|
||||
import { ExpressionPracticeCardView } from "./ExpressionPracticeCardView";
|
||||
|
||||
export function ExpressionPracticeView() {
|
||||
@ -16,7 +17,7 @@ export function ExpressionPracticeView() {
|
||||
const expressions = useExpressionsByExpressionSetId(expression_set_id);
|
||||
// TODO handle errors
|
||||
|
||||
if (!expressions) return null; // LOADING
|
||||
if (!expressions) return <LoadingView />;
|
||||
|
||||
const filtered_expressions = expressions.filter(
|
||||
(expression) => !filter_ids.includes(expression.id!)
|
||||
|
@ -8,13 +8,14 @@ import {
|
||||
import { IndexedExpressionSet, ItemNotFoundError } from "../../model";
|
||||
import { AppPath, AppRouting } from "../../model/routing";
|
||||
import { ErrorView } from "../ErrorView";
|
||||
import { LoadingView } from "../LoadingView/LoadingView";
|
||||
|
||||
export function ExpressionSetDetailsView() {
|
||||
const expression_set_id = useQueryExpressionSetId();
|
||||
const expression_set = useExpressionSetById(expression_set_id);
|
||||
const expressions = useExpressionsByExpressionSetId(expression_set_id);
|
||||
|
||||
if (!expression_set || !expressions) return null; // LOADING
|
||||
if (!expression_set || !expressions) return <LoadingView />;
|
||||
if (expression_set === ItemNotFoundError) {
|
||||
return <ErrorView message="Expression set not found" />;
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { useAllExpressionSets } from "../../hooks";
|
||||
import { ErrorView } from "../ErrorView";
|
||||
import { LoadingView } from "../LoadingView/LoadingView";
|
||||
import { ExpressionSetLink } from "./ExpressionSetLink";
|
||||
|
||||
export function ExpressionSetListView() {
|
||||
const expression_sets = useAllExpressionSets();
|
||||
|
||||
if (!expression_sets) return null; // LOADING
|
||||
if (!expression_sets) return <LoadingView />;
|
||||
if (!expression_sets.length) {
|
||||
return <ErrorView message="No expression sets found" />;
|
||||
}
|
||||
|
13
src/views/LoadingView/LoadingView.tsx
Normal file
13
src/views/LoadingView/LoadingView.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
export function LoadingView() {
|
||||
return (
|
||||
<div className="page-loading">
|
||||
<img
|
||||
className="loading-icon"
|
||||
src="/icons/rotate-clockwise.svg"
|
||||
width="64"
|
||||
height="64"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
1
src/views/LoadingView/index.ts
Normal file
1
src/views/LoadingView/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./LoadingView";
|
Loading…
Reference in New Issue
Block a user