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