Add loading view component #8
							
								
								
									
										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;
 | 
					  --gap-large: 32px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  --duration-short: 0.3s;
 | 
					  --duration-short: 0.3s;
 | 
				
			||||||
 | 
					  --duration-long: 1.5s;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.grow {
 | 
					.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 buttons */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.icon-button {
 | 
					.icon-button {
 | 
				
			||||||
@ -306,6 +332,14 @@ i {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/* Expression set page */
 | 
					/* Expression set page */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.page-loading {
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.page-with-padding {
 | 
					.page-with-padding {
 | 
				
			||||||
  padding: var(--gap-small) var(--gap-medium) var(--gap-medium);
 | 
					  padding: var(--gap-small) var(--gap-medium) var(--gap-medium);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import { useAllExpressions } from "../../hooks";
 | 
				
			|||||||
import { IndexedExpression } from "../../model";
 | 
					import { IndexedExpression } from "../../model";
 | 
				
			||||||
import { AppPath, AppRouting } from "../../model/routing";
 | 
					import { AppPath, AppRouting } from "../../model/routing";
 | 
				
			||||||
import { ErrorView } from "../ErrorView";
 | 
					import { ErrorView } from "../ErrorView";
 | 
				
			||||||
 | 
					import { LoadingView } from "../LoadingView/LoadingView";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ExpressionCardListView() {
 | 
					export function ExpressionCardListView() {
 | 
				
			||||||
  const { setRoute } = useContext(AppRouting);
 | 
					  const { setRoute } = useContext(AppRouting);
 | 
				
			||||||
@ -13,7 +14,7 @@ export function ExpressionCardListView() {
 | 
				
			|||||||
    [expressions]
 | 
					    [expressions]
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!expressions) return null; // LOADING
 | 
					  if (!expressions) return <LoadingView />;
 | 
				
			||||||
  if (!expressions.length) {
 | 
					  if (!expressions.length) {
 | 
				
			||||||
    return <ErrorView message="No expression cards yet" />;
 | 
					    return <ErrorView message="No expression cards yet" />;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -3,12 +3,13 @@ import { ExpressionDescription } from "../../components/ExpressionDescription";
 | 
				
			|||||||
import { useExpressionById, useQueryExpressionId } from "../../hooks";
 | 
					import { useExpressionById, useQueryExpressionId } from "../../hooks";
 | 
				
			||||||
import { ItemNotFoundError } from "../../model";
 | 
					import { ItemNotFoundError } from "../../model";
 | 
				
			||||||
import { ErrorView } from "../ErrorView";
 | 
					import { ErrorView } from "../ErrorView";
 | 
				
			||||||
 | 
					import { LoadingView } from "../LoadingView/LoadingView";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ExpressionCardView() {
 | 
					export function ExpressionCardView() {
 | 
				
			||||||
  const expression_id = useQueryExpressionId();
 | 
					  const expression_id = useQueryExpressionId();
 | 
				
			||||||
  const expression = useExpressionById(expression_id);
 | 
					  const expression = useExpressionById(expression_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (expression === undefined) return null; // LOADING
 | 
					  if (expression === undefined) return <LoadingView />;
 | 
				
			||||||
  if (expression === ItemNotFoundError)
 | 
					  if (expression === ItemNotFoundError)
 | 
				
			||||||
    return <ErrorView message="Expression card not found" />;
 | 
					    return <ErrorView message="Expression card not found" />;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import {
 | 
				
			|||||||
import { IndexedExpression } from "../../model";
 | 
					import { IndexedExpression } from "../../model";
 | 
				
			||||||
import { sample } from "../../util";
 | 
					import { sample } from "../../util";
 | 
				
			||||||
import { ErrorView } from "../ErrorView";
 | 
					import { ErrorView } from "../ErrorView";
 | 
				
			||||||
 | 
					import { LoadingView } from "../LoadingView/LoadingView";
 | 
				
			||||||
import { ExpressionPracticeCardView } from "./ExpressionPracticeCardView";
 | 
					import { ExpressionPracticeCardView } from "./ExpressionPracticeCardView";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ExpressionPracticeView() {
 | 
					export function ExpressionPracticeView() {
 | 
				
			||||||
@ -16,7 +17,7 @@ export function ExpressionPracticeView() {
 | 
				
			|||||||
  const expressions = useExpressionsByExpressionSetId(expression_set_id);
 | 
					  const expressions = useExpressionsByExpressionSetId(expression_set_id);
 | 
				
			||||||
  // TODO handle errors
 | 
					  // TODO handle errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!expressions) return null; // LOADING
 | 
					  if (!expressions) return <LoadingView />;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const filtered_expressions = expressions.filter(
 | 
					  const filtered_expressions = expressions.filter(
 | 
				
			||||||
    (expression) => !filter_ids.includes(expression.id!)
 | 
					    (expression) => !filter_ids.includes(expression.id!)
 | 
				
			||||||
 | 
				
			|||||||
@ -8,13 +8,14 @@ import {
 | 
				
			|||||||
import { IndexedExpressionSet, ItemNotFoundError } from "../../model";
 | 
					import { IndexedExpressionSet, ItemNotFoundError } from "../../model";
 | 
				
			||||||
import { AppPath, AppRouting } from "../../model/routing";
 | 
					import { AppPath, AppRouting } from "../../model/routing";
 | 
				
			||||||
import { ErrorView } from "../ErrorView";
 | 
					import { ErrorView } from "../ErrorView";
 | 
				
			||||||
 | 
					import { LoadingView } from "../LoadingView/LoadingView";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ExpressionSetDetailsView() {
 | 
					export function ExpressionSetDetailsView() {
 | 
				
			||||||
  const expression_set_id = useQueryExpressionSetId();
 | 
					  const expression_set_id = useQueryExpressionSetId();
 | 
				
			||||||
  const expression_set = useExpressionSetById(expression_set_id);
 | 
					  const expression_set = useExpressionSetById(expression_set_id);
 | 
				
			||||||
  const expressions = useExpressionsByExpressionSetId(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) {
 | 
					  if (expression_set === ItemNotFoundError) {
 | 
				
			||||||
    return <ErrorView message="Expression set not found" />;
 | 
					    return <ErrorView message="Expression set not found" />;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,12 @@
 | 
				
			|||||||
import { useAllExpressionSets } from "../../hooks";
 | 
					import { useAllExpressionSets } from "../../hooks";
 | 
				
			||||||
import { ErrorView } from "../ErrorView";
 | 
					import { ErrorView } from "../ErrorView";
 | 
				
			||||||
 | 
					import { LoadingView } from "../LoadingView/LoadingView";
 | 
				
			||||||
import { ExpressionSetLink } from "./ExpressionSetLink";
 | 
					import { ExpressionSetLink } from "./ExpressionSetLink";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ExpressionSetListView() {
 | 
					export function ExpressionSetListView() {
 | 
				
			||||||
  const expression_sets = useAllExpressionSets();
 | 
					  const expression_sets = useAllExpressionSets();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!expression_sets) return null; // LOADING
 | 
					  if (!expression_sets) return <LoadingView />;
 | 
				
			||||||
  if (!expression_sets.length) {
 | 
					  if (!expression_sets.length) {
 | 
				
			||||||
    return <ErrorView message="No expression sets found" />;
 | 
					    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