Compare commits
26 Commits
a4692a7d1f
...
develop/mo
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b9fbe4e476 | ||
![]() |
0d844eed3f | ||
![]() |
2862cddb6b | ||
![]() |
55509b483c | ||
![]() |
3f2ac2a285 | ||
![]() |
1216de4ff9 | ||
![]() |
7966d8abf5 | ||
![]() |
e975aebfcd | ||
![]() |
7f6970b183 | ||
![]() |
e83854ed2a | ||
![]() |
6961f058df | ||
![]() |
b515337acc | ||
![]() |
c732d89c34 | ||
![]() |
2b71d06c8d | ||
![]() |
3ec9d87f8b | ||
![]() |
546fb72732 | ||
![]() |
4303704753 | ||
![]() |
4bb213ef0c | ||
![]() |
657c652657 | ||
![]() |
5be77aae23 | ||
![]() |
ffbe63e421 | ||
![]() |
ba1e718039 | ||
![]() |
8b32d153db | ||
![]() |
d3e554b218 | ||
![]() |
5b10977a64 | ||
![]() |
a40b0fa4b1 |
@@ -14,9 +14,9 @@ import {
|
|||||||
|
|
||||||
import { Geolocation } from '@capacitor/geolocation';
|
import { Geolocation } from '@capacitor/geolocation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { SkeletonDashboard } from '../components/SkeletonDashboard';
|
import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard';
|
||||||
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
|
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
|
||||||
import { CurrentWeather } from '../components/CurrentWeather';
|
import { CurrentWeather } from '../TestComponents/CurrentWeather';
|
||||||
|
|
||||||
function Tab1() {
|
function Tab1() {
|
||||||
const router = useIonRouter();
|
const router = useIonRouter();
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
IonToolbar,
|
IonToolbar,
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CurrentWeather } from '../components/CurrentWeather';
|
import { CurrentWeather } from '../TestComponents/CurrentWeather';
|
||||||
|
|
||||||
function Tab2() {
|
function Tab2() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
.keypad {
|
||||||
|
bottom: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
118
03_source/mobile/src/pages/Demo2FaExample/components/Keypad.tsx
Normal file
118
03_source/mobile/src/pages/Demo2FaExample/components/Keypad.tsx
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { IonRow } from "@ionic/react";
|
||||||
|
import styles from "./Keypad.module.scss";
|
||||||
|
import KeypadButton from "./KeypadButton";
|
||||||
|
|
||||||
|
const Keypad = (props: any): JSX.Element => {
|
||||||
|
const { activeIndex, handleClick, handleRemove, amount, correct } = props;
|
||||||
|
|
||||||
|
const keypadButtons = [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: "1",
|
||||||
|
handleClick: () => handleClick(activeIndex, 1),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "2",
|
||||||
|
handleClick: () => handleClick(activeIndex, 2),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "3",
|
||||||
|
handleClick: () => handleClick(activeIndex, 3),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: "4",
|
||||||
|
handleClick: () => handleClick(activeIndex, 4),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "5",
|
||||||
|
handleClick: () => handleClick(activeIndex, 5),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "6",
|
||||||
|
handleClick: () => handleClick(activeIndex, 6),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: "7",
|
||||||
|
handleClick: () => handleClick(activeIndex, 7),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "8",
|
||||||
|
handleClick: () => handleClick(activeIndex, 8),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "9",
|
||||||
|
handleClick: () => handleClick(activeIndex, 9),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: "Resend",
|
||||||
|
handleClick: () => handleClick(activeIndex, 1),
|
||||||
|
small: true,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "0",
|
||||||
|
handleClick: () => handleClick(activeIndex, 2),
|
||||||
|
small: false,
|
||||||
|
remove: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "",
|
||||||
|
handleClick: () => handleRemove(),
|
||||||
|
small: true,
|
||||||
|
remove: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`${styles.keypad}`}>
|
||||||
|
{keypadButtons.map((keypadRow, index) => {
|
||||||
|
const isDisabled = parseInt(activeIndex) === parseInt(amount);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonRow key={`keypadRow_${index}`}>
|
||||||
|
{keypadRow.map((button, index2) => {
|
||||||
|
return (
|
||||||
|
<KeypadButton
|
||||||
|
correct={correct}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
key={`keypadButton_${index2}`}
|
||||||
|
value={button.value}
|
||||||
|
handleClick={button.handleClick}
|
||||||
|
small={button.small}
|
||||||
|
remove={button.remove}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</IonRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Keypad;
|
@@ -0,0 +1,30 @@
|
|||||||
|
.logo {
|
||||||
|
height: 4rem;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keypadButton {
|
||||||
|
--background: none;
|
||||||
|
--color: black;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
--outline: none;
|
||||||
|
--border: none;
|
||||||
|
--box-shadow: none;
|
||||||
|
padding: none;
|
||||||
|
margin: none;
|
||||||
|
--background-hover: rgb(245, 245, 245) !important;
|
||||||
|
--background-focused: rgb(245, 245, 245) !important;
|
||||||
|
--background-activated: rgb(245, 245, 245) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallKeypadButton {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keypad {
|
||||||
|
bottom: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
import { IonButton, IonCol, IonIcon } from "@ionic/react";
|
||||||
|
import { backspaceOutline } from "ionicons/icons";
|
||||||
|
import styles from "./KeypadButton.module.scss";
|
||||||
|
|
||||||
|
const KeypadButton = (props: any): JSX.Element => {
|
||||||
|
const {
|
||||||
|
small,
|
||||||
|
value,
|
||||||
|
remove,
|
||||||
|
handleClick,
|
||||||
|
isDisabled = false,
|
||||||
|
correct,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonCol size="4" className={styles.keypadButton}>
|
||||||
|
<IonButton
|
||||||
|
disabled={(!small || correct) && isDisabled}
|
||||||
|
className={`${styles.keypadButton} ${
|
||||||
|
small && styles.smallKeypadButton
|
||||||
|
}`}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{!remove && value}
|
||||||
|
{remove && <IonIcon icon={backspaceOutline} />}
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KeypadButton;
|
@@ -0,0 +1,38 @@
|
|||||||
|
.keypadInput {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 2.5rem;
|
||||||
|
background-color: rgb(245, 245, 245);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: rgb(207, 207, 207);
|
||||||
|
transition: 0.2s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color: rgba(26, 150, 251, 0.2);
|
||||||
|
border: 0 !important;
|
||||||
|
color: white !important;
|
||||||
|
transition: 0.2s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filled {
|
||||||
|
color: rgb(151, 151, 151);
|
||||||
|
transition: 0.2s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incorrect {
|
||||||
|
background-color: rgba(251, 26, 26, 0.2);
|
||||||
|
color: rgb(218, 67, 67);
|
||||||
|
transition: 0.2s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.correct {
|
||||||
|
background-color: rgba(26, 251, 120, 0.2);
|
||||||
|
color: rgb(67, 218, 112);
|
||||||
|
transition: 0.2s linear;
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
import { IonCol } from "@ionic/react";
|
||||||
|
import styles from "./KeypadInput.module.scss";
|
||||||
|
|
||||||
|
const KeypadInput = (props: any): JSX.Element => {
|
||||||
|
const {
|
||||||
|
value,
|
||||||
|
isActive = false,
|
||||||
|
isFilled = false,
|
||||||
|
incorrect,
|
||||||
|
correct,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonCol size="2">
|
||||||
|
<div
|
||||||
|
className={`${styles.keypadInput} ${isActive && styles.active} ${
|
||||||
|
isFilled && styles.filled
|
||||||
|
} ${incorrect && styles.incorrect} ${correct && styles.correct}`}
|
||||||
|
onClick={props.handleChange}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
{!isFilled && "0"}
|
||||||
|
</div>
|
||||||
|
</IonCol>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KeypadInput;
|
@@ -0,0 +1,50 @@
|
|||||||
|
import { IonRow } from "@ionic/react";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import KeypadInput from "./KeypadInput";
|
||||||
|
|
||||||
|
const KeypadInputs = (props: any): JSX.Element => {
|
||||||
|
const { values, activeIndex, incorrect, correct } = props;
|
||||||
|
const keypadRef = useRef<HTMLIonRowElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (incorrect && keypadRef.current) {
|
||||||
|
keypadRef.current.classList.add("incorrect");
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (keypadRef.current) {
|
||||||
|
keypadRef.current.classList.remove("incorrect");
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}, [incorrect]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (correct) {
|
||||||
|
if (keypadRef.current) {
|
||||||
|
keypadRef.current.classList.add("correct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [correct]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonRow ref={keypadRef} className="ion-justify-content-center">
|
||||||
|
{values.map((value: string, index: number) => {
|
||||||
|
const isActive = parseInt(index.toString()) === parseInt(activeIndex);
|
||||||
|
const isFilled = value !== "" ? true : false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<KeypadInput
|
||||||
|
correct={correct}
|
||||||
|
incorrect={incorrect}
|
||||||
|
isFilled={isFilled}
|
||||||
|
isActive={isActive}
|
||||||
|
value={value}
|
||||||
|
placeholder="0"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</IonRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KeypadInputs;
|
@@ -1,38 +1,19 @@
|
|||||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
import { IonRouterOutlet, IonTabs } from '@ionic/react';
|
||||||
|
|
||||||
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
|
||||||
import { Route, Redirect } from 'react-router';
|
import { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
import Home from './pages/Home';
|
||||||
import Tab2 from './AppPages/Tab2';
|
|
||||||
|
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
function Demo2FaExample() {
|
function Demo2FaExample() {
|
||||||
return (
|
return (
|
||||||
<IonTabs>
|
<IonTabs className="demo-2fa-example">
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path="/demo-weather-app/tab1">
|
<Route exact path="/demo-2fa-example/home">
|
||||||
<Tab1 />
|
<Home />
|
||||||
</Route>
|
|
||||||
<Route exact path="/demo-weather-app/tab2">
|
|
||||||
<Tab2 />
|
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Redirect exact path="/demo-weather-app" to="/demo-weather-app/tab1" />
|
<Redirect exact path="/demo-2fa-example" to="/demo-2fa-example/home" />
|
||||||
</IonRouterOutlet>
|
</IonRouterOutlet>
|
||||||
|
|
||||||
{/* */}
|
|
||||||
<IonTabBar slot="bottom">
|
|
||||||
<IonTabButton tab="tab1" href="/demo-weather-app/tab1">
|
|
||||||
<IonIcon icon={cloudOutline} />
|
|
||||||
<IonLabel>Dashboard</IonLabel>
|
|
||||||
</IonTabButton>
|
|
||||||
<IonTabButton tab="tab2" href="/demo-weather-app/tab2">
|
|
||||||
<IonIcon icon={searchOutline} />
|
|
||||||
<IonLabel>Search</IonLabel>
|
|
||||||
</IonTabButton>
|
|
||||||
</IonTabBar>
|
|
||||||
</IonTabs>
|
</IonTabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,30 @@
|
|||||||
|
.logo {
|
||||||
|
height: 4rem;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incorrect {
|
||||||
|
color: rgb(218, 67, 67);
|
||||||
|
}
|
||||||
|
|
||||||
|
.successContainer {
|
||||||
|
}
|
||||||
|
|
||||||
|
.successText {
|
||||||
|
background-color: rgba(26, 251, 120, 0.2);
|
||||||
|
color: rgb(67, 218, 112);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.successContinue {
|
||||||
|
font-weight: 700;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.successContinue ion-icon {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
}
|
135
03_source/mobile/src/pages/Demo2FaExample/pages/Home.tsx
Normal file
135
03_source/mobile/src/pages/Demo2FaExample/pages/Home.tsx
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import {
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonImg,
|
||||||
|
IonPage,
|
||||||
|
IonRow,
|
||||||
|
IonToolbar,
|
||||||
|
useIonRouter,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { arrowForwardOutline, chevronBackOutline } from 'ionicons/icons';
|
||||||
|
import styles from './Home.module.scss';
|
||||||
|
import KeypadInputs from '../components/KeypadInputs';
|
||||||
|
import Keypad from '../components/Keypad';
|
||||||
|
import { JSX, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
const Home = (): JSX.Element => {
|
||||||
|
const correctCode = [5, 9, 2, 5];
|
||||||
|
const [keypadValues, setKeypadValues] = useState(['', '', '', '']);
|
||||||
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
|
const successRef = useRef<HTMLIonRowElement>(null);
|
||||||
|
|
||||||
|
const [incorrect, setIncorrect] = useState(false);
|
||||||
|
const [correct, setCorrect] = useState(false);
|
||||||
|
|
||||||
|
const tempValues: { [key: string]: any } = {};
|
||||||
|
|
||||||
|
const handleClick = (index: number, value: any) => {
|
||||||
|
const stringKey = index.toString();
|
||||||
|
tempValues[stringKey] = value;
|
||||||
|
|
||||||
|
setKeypadValues(value);
|
||||||
|
setActiveIndex((activeIndex) => activeIndex + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemove = () => {
|
||||||
|
const tempValues = [...keypadValues];
|
||||||
|
tempValues[activeIndex - 1] = '';
|
||||||
|
|
||||||
|
setKeypadValues(tempValues);
|
||||||
|
activeIndex > 0 && setActiveIndex((activeIndex) => activeIndex - 1);
|
||||||
|
setIncorrect(false);
|
||||||
|
setCorrect(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = useIonRouter();
|
||||||
|
function handleBackClick() {
|
||||||
|
router.goBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (parseInt(activeIndex.toString()) === parseInt(keypadValues.length.toString())) {
|
||||||
|
var error = false;
|
||||||
|
|
||||||
|
keypadValues.forEach((value, index) => {
|
||||||
|
if (parseInt(value) !== parseInt(correctCode[index].toString())) {
|
||||||
|
error = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
setIncorrect(true);
|
||||||
|
} else {
|
||||||
|
setCorrect(true);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (successRef.current) {
|
||||||
|
successRef.current.classList.remove('hidden');
|
||||||
|
successRef.current.classList.add('success');
|
||||||
|
}
|
||||||
|
}, 900);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeIndex]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonHeader></IonHeader>
|
||||||
|
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton shape={'round'} onClick={() => handleBackClick()}>
|
||||||
|
<IonIcon icon={chevronBackOutline} color="primary" />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid className="ion-text-center ion-padding-top">
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonImg src="/assets/icon/favicon.png" className={styles.logo} />
|
||||||
|
<h1>Verification required</h1>
|
||||||
|
<p>Enter your 4 digit verification code</p>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow ref={successRef} className="ion-justify-content-center hidden">
|
||||||
|
<IonCol size="12" className={styles.successContainer}>
|
||||||
|
<p className={styles.successText}>
|
||||||
|
Awesome! You may continue.
|
||||||
|
<br />
|
||||||
|
<span className={styles.successContinue}>
|
||||||
|
Continue
|
||||||
|
<IonIcon icon={arrowForwardOutline} />
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<KeypadInputs
|
||||||
|
incorrect={incorrect}
|
||||||
|
correct={correct}
|
||||||
|
values={keypadValues}
|
||||||
|
activeIndex={activeIndex}
|
||||||
|
/>
|
||||||
|
{incorrect && <p className={styles.incorrect}>Wrong code entered</p>}
|
||||||
|
<Keypad
|
||||||
|
handleRemove={handleRemove}
|
||||||
|
handleClick={handleClick}
|
||||||
|
activeIndex={activeIndex}
|
||||||
|
amount={keypadValues.length}
|
||||||
|
correct={correct}
|
||||||
|
/>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
@@ -1,103 +0,0 @@
|
|||||||
#about-page {
|
|
||||||
ion-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
--background: transparent;
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-toolbar ion-back-button,
|
|
||||||
ion-toolbar ion-button,
|
|
||||||
ion-toolbar ion-menu-button {
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .about-image {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
transition: opacity 500ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .madison {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .austin {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .chicago {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .seattle {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info {
|
|
||||||
position: relative;
|
|
||||||
margin-top: -10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--ion-background-color, #fff);
|
|
||||||
z-index: 2; // display rounded border above header image
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-list {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info p {
|
|
||||||
line-height: 130%;
|
|
||||||
|
|
||||||
color: var(--ion-color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-icon {
|
|
||||||
margin-inline-end: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios .about-info {
|
|
||||||
--ion-padding: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios .about-info h3 {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#date-input-popover {
|
|
||||||
--offset-y: -var(--ion-safe-area-bottom);
|
|
||||||
|
|
||||||
--max-width: 90%;
|
|
||||||
--width: 336px;
|
|
||||||
}
|
|
249
03_source/mobile/src/pages/Demo2FaExample/theme/variables.scss
Normal file
249
03_source/mobile/src/pages/Demo2FaExample/theme/variables.scss
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
.demo-2fa-example {
|
||||||
|
/* Ionic Variables and Theming. For more info, please see:
|
||||||
|
http://ionicframework.com/docs/theming/ */
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-family: 'Lato', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
transition: 0.2s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ionic CSS Variables **/
|
||||||
|
:root {
|
||||||
|
/** primary **/
|
||||||
|
--ion-color-primary: #3880ff;
|
||||||
|
--ion-color-primary-rgb: 56, 128, 255;
|
||||||
|
--ion-color-primary-contrast: #ffffff;
|
||||||
|
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-primary-shade: #3171e0;
|
||||||
|
--ion-color-primary-tint: #4c8dff;
|
||||||
|
|
||||||
|
/** secondary **/
|
||||||
|
--ion-color-secondary: #3dc2ff;
|
||||||
|
--ion-color-secondary-rgb: 61, 194, 255;
|
||||||
|
--ion-color-secondary-contrast: #ffffff;
|
||||||
|
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-secondary-shade: #36abe0;
|
||||||
|
--ion-color-secondary-tint: #50c8ff;
|
||||||
|
|
||||||
|
/** tertiary **/
|
||||||
|
--ion-color-tertiary: #5260ff;
|
||||||
|
--ion-color-tertiary-rgb: 82, 96, 255;
|
||||||
|
--ion-color-tertiary-contrast: #ffffff;
|
||||||
|
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-tertiary-shade: #4854e0;
|
||||||
|
--ion-color-tertiary-tint: #6370ff;
|
||||||
|
|
||||||
|
/** success **/
|
||||||
|
--ion-color-success: #2dd36f;
|
||||||
|
--ion-color-success-rgb: 45, 211, 111;
|
||||||
|
--ion-color-success-contrast: #ffffff;
|
||||||
|
--ion-color-success-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-success-shade: #28ba62;
|
||||||
|
--ion-color-success-tint: #42d77d;
|
||||||
|
|
||||||
|
/** warning **/
|
||||||
|
--ion-color-warning: #ffc409;
|
||||||
|
--ion-color-warning-rgb: 255, 196, 9;
|
||||||
|
--ion-color-warning-contrast: #000000;
|
||||||
|
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-warning-shade: #e0ac08;
|
||||||
|
--ion-color-warning-tint: #ffca22;
|
||||||
|
|
||||||
|
/** danger **/
|
||||||
|
--ion-color-danger: #eb445a;
|
||||||
|
--ion-color-danger-rgb: 235, 68, 90;
|
||||||
|
--ion-color-danger-contrast: #ffffff;
|
||||||
|
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-danger-shade: #cf3c4f;
|
||||||
|
--ion-color-danger-tint: #ed576b;
|
||||||
|
|
||||||
|
/** dark **/
|
||||||
|
--ion-color-dark: #222428;
|
||||||
|
--ion-color-dark-rgb: 34, 36, 40;
|
||||||
|
--ion-color-dark-contrast: #ffffff;
|
||||||
|
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-dark-shade: #1e2023;
|
||||||
|
--ion-color-dark-tint: #383a3e;
|
||||||
|
|
||||||
|
/** medium **/
|
||||||
|
--ion-color-medium: #92949c;
|
||||||
|
--ion-color-medium-rgb: 146, 148, 156;
|
||||||
|
--ion-color-medium-contrast: #ffffff;
|
||||||
|
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-medium-shade: #808289;
|
||||||
|
--ion-color-medium-tint: #9d9fa6;
|
||||||
|
|
||||||
|
/** light **/
|
||||||
|
--ion-color-light: #f4f5f8;
|
||||||
|
--ion-color-light-rgb: 244, 245, 248;
|
||||||
|
--ion-color-light-contrast: #000000;
|
||||||
|
--ion-color-light-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-light-shade: #d7d8da;
|
||||||
|
--ion-color-light-tint: #f5f6f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incorrect {
|
||||||
|
-webkit-animation: incorrect-animation 0.9s both;
|
||||||
|
animation: incorrect-animation 0.9s both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes incorrect-animation {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale3d(1, 1, 1);
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
30% {
|
||||||
|
-webkit-transform: scale3d(1.25, 0.75, 1);
|
||||||
|
transform: scale3d(1.25, 0.75, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
-webkit-transform: scale3d(0.75, 1.25, 1);
|
||||||
|
transform: scale3d(0.75, 1.25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
-webkit-transform: scale3d(1.15, 0.85, 1);
|
||||||
|
transform: scale3d(1.15, 0.85, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
65% {
|
||||||
|
-webkit-transform: scale3d(0.95, 1.05, 1);
|
||||||
|
transform: scale3d(0.95, 1.05, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
-webkit-transform: scale3d(1.05, 0.95, 1);
|
||||||
|
transform: scale3d(1.05, 0.95, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale3d(1, 1, 1);
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes incorrect-animation {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale3d(1, 1, 1);
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
30% {
|
||||||
|
-webkit-transform: scale3d(1.25, 0.75, 1);
|
||||||
|
transform: scale3d(1.25, 0.75, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
40% {
|
||||||
|
-webkit-transform: scale3d(0.75, 1.25, 1);
|
||||||
|
transform: scale3d(0.75, 1.25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
-webkit-transform: scale3d(1.15, 0.85, 1);
|
||||||
|
transform: scale3d(1.15, 0.85, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
65% {
|
||||||
|
-webkit-transform: scale3d(0.95, 1.05, 1);
|
||||||
|
transform: scale3d(0.95, 1.05, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
-webkit-transform: scale3d(1.05, 0.95, 1);
|
||||||
|
transform: scale3d(1.05, 0.95, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale3d(1, 1, 1);
|
||||||
|
transform: scale3d(1, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.correct {
|
||||||
|
-webkit-animation: correct-animation 1s ease-in both;
|
||||||
|
animation: correct-animation 1s ease-in both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes correct-animation {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translateY(0) rotateX(0) scale(1);
|
||||||
|
transform: translateY(0) rotateX(0) scale(1);
|
||||||
|
-webkit-transform-origin: 50% 1400px;
|
||||||
|
transform-origin: 50% 1400px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
-webkit-transform: translateY(-600px) rotateX(-30deg) scale(0);
|
||||||
|
transform: translateY(-600px) rotateX(-30deg) scale(0);
|
||||||
|
-webkit-transform-origin: 50% 100%;
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes correct-animation {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translateY(0) rotateX(0) scale(1);
|
||||||
|
transform: translateY(0) rotateX(0) scale(1);
|
||||||
|
-webkit-transform-origin: 50% 1400px;
|
||||||
|
transform-origin: 50% 1400px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
-webkit-transform: translateY(-600px) rotateX(-30deg) scale(0);
|
||||||
|
transform: translateY(-600px) rotateX(-30deg) scale(0);
|
||||||
|
-webkit-transform-origin: 50% 100%;
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
-webkit-animation: success-animation 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||||
|
animation: success-animation 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes success-animation {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translateY(-600px) rotateX(-30deg) scale(0);
|
||||||
|
transform: translateY(-600px) rotateX(-30deg) scale(0);
|
||||||
|
-webkit-transform-origin: 50% 100%;
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
-webkit-transform: translateY(0) rotateX(0) scale(1);
|
||||||
|
transform: translateY(0) rotateX(0) scale(1);
|
||||||
|
-webkit-transform-origin: 50% 1400px;
|
||||||
|
transform-origin: 50% 1400px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes success-animation {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: translateY(-600px) rotateX(-30deg) scale(0);
|
||||||
|
transform: translateY(-600px) rotateX(-30deg) scale(0);
|
||||||
|
-webkit-transform-origin: 50% 100%;
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
-webkit-transform: translateY(0) rotateX(0) scale(1);
|
||||||
|
transform: translateY(0) rotateX(0) scale(1);
|
||||||
|
-webkit-transform-origin: 50% 1400px;
|
||||||
|
transform-origin: 50% 1400px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -13,8 +13,9 @@ import {
|
|||||||
IonThumbnail,
|
IonThumbnail,
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
|
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
export const SkeletonDashboard = () => (
|
export const SkeletonDashboard = (): React.JSX.Element => (
|
||||||
<IonGrid>
|
<IonGrid>
|
||||||
<IonCard>
|
<IonCard>
|
||||||
<IonCardContent className="ion-text-center">
|
<IonCardContent className="ion-text-center">
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
import { IonRouterOutlet, IonTabs } from '@ionic/react';
|
||||||
|
|
||||||
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
|
||||||
import { Route, Redirect } from 'react-router';
|
import { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
// import Tab1 from './AppPages/Tab1';
|
||||||
import Tab2 from './AppPages/Tab2';
|
// import Tab2 from './AppPages/Tab2';
|
||||||
|
|
||||||
import Home from './pages/Home.jsx';
|
import Home from './pages/Home';
|
||||||
import Account from './pages/Account';
|
import Account from './pages/Account';
|
||||||
import AddCard from './pages/AddCard';
|
import AddCard from './pages/AddCard';
|
||||||
import AddTransaction from './pages/AddTransaction';
|
import AddTransaction from './pages/AddTransaction';
|
||||||
|
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
function DemoBankingUi() {
|
function DemoBankingUi(): React.JSX.Element {
|
||||||
return (
|
return (
|
||||||
<IonTabs className="demo-banking-ui">
|
<IonTabs className="demo-banking-ui">
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
|
@@ -1,5 +1,17 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { IonButton, IonButtons, IonContent, IonGrid, IonHeader, IonIcon, IonPage, IonTitle, IonToolbar, useIonRouter, useIonViewDidEnter } from '@ionic/react';
|
import {
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonPage,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
useIonRouter,
|
||||||
|
useIonViewDidEnter,
|
||||||
|
} from '@ionic/react';
|
||||||
import styles from './Home.module.css';
|
import styles from './Home.module.css';
|
||||||
import { AccountStore } from '../data/AccountStore';
|
import { AccountStore } from '../data/AccountStore';
|
||||||
import CardSlide from '../components/CardSlide';
|
import CardSlide from '../components/CardSlide';
|
||||||
@@ -22,7 +34,7 @@ const Home = () => {
|
|||||||
const [mainColor, setMainColor] = useState(cards[0].color);
|
const [mainColor, setMainColor] = useState(cards[0].color);
|
||||||
const [slideSpace, setSlideSpace] = useState(10);
|
const [slideSpace, setSlideSpace] = useState(10);
|
||||||
|
|
||||||
const slidesRef = useRef();
|
const slidesRef = useRef(null);
|
||||||
|
|
||||||
useIonViewDidEnter(() => {
|
useIonViewDidEnter(() => {
|
||||||
setSlideSpace(0);
|
setSlideSpace(0);
|
||||||
@@ -38,7 +50,9 @@ const Home = () => {
|
|||||||
document.getElementById(`slide_${swiperIndex}_balance`).classList.add('animate__headShake');
|
document.getElementById(`slide_${swiperIndex}_balance`).classList.add('animate__headShake');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.getElementById(`slide_${swiperIndex}_balance`).classList.remove('animate__headShake');
|
document
|
||||||
|
.getElementById(`slide_${swiperIndex}_balance`)
|
||||||
|
.classList.remove('animate__headShake');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -47,9 +61,13 @@ const Home = () => {
|
|||||||
const swiperIndex = swiper.activeIndex;
|
const swiperIndex = swiper.activeIndex;
|
||||||
|
|
||||||
if (touched) {
|
if (touched) {
|
||||||
document.getElementById(`slide_${swiperIndex}_transactions`).classList.add('animate__fadeOut');
|
document
|
||||||
|
.getElementById(`slide_${swiperIndex}_transactions`)
|
||||||
|
.classList.add('animate__fadeOut');
|
||||||
} else {
|
} else {
|
||||||
document.getElementById(`slide_${swiperIndex}_transactions`).classList.remove('animate__fadeOut');
|
document
|
||||||
|
.getElementById(`slide_${swiperIndex}_transactions`)
|
||||||
|
.classList.remove('animate__fadeOut');
|
||||||
document.getElementById(`slide_${swiperIndex}_transactions`).classList.add('animate__fadeIn');
|
document.getElementById(`slide_${swiperIndex}_transactions`).classList.add('animate__fadeIn');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -70,7 +88,11 @@ const Home = () => {
|
|||||||
routerLink="/demo-banking-ui/account"
|
routerLink="/demo-banking-ui/account"
|
||||||
className={stylesS.toolbarAvatar}
|
className={stylesS.toolbarAvatar}
|
||||||
>
|
>
|
||||||
<img alt="toolbar avatar" className={stylesS.toolbarAvatarImage} src={profile.avatar} />
|
<img
|
||||||
|
alt="toolbar avatar"
|
||||||
|
className={stylesS.toolbarAvatarImage}
|
||||||
|
src={profile.avatar}
|
||||||
|
/>
|
||||||
</IonButton>
|
</IonButton>
|
||||||
</IonButtons>
|
</IonButtons>
|
||||||
|
|
||||||
@@ -82,7 +104,11 @@ const Home = () => {
|
|||||||
</IonButton>
|
</IonButton>
|
||||||
{/* */}
|
{/* */}
|
||||||
<IonButton>
|
<IonButton>
|
||||||
<IonIcon color="light" icon={searchOutline} style={{ backgroundColor: mainColor, borderRadius: '500px', padding: '0.2rem' }} />
|
<IonIcon
|
||||||
|
color="light"
|
||||||
|
icon={searchOutline}
|
||||||
|
style={{ backgroundColor: mainColor, borderRadius: '500px', padding: '0.2rem' }}
|
||||||
|
/>
|
||||||
</IonButton>
|
</IonButton>
|
||||||
</IonButtons>
|
</IonButtons>
|
||||||
</IonToolbar>
|
</IonToolbar>
|
||||||
@@ -101,7 +127,11 @@ const Home = () => {
|
|||||||
>
|
>
|
||||||
{cards.map((card, index) => {
|
{cards.map((card, index) => {
|
||||||
return (
|
return (
|
||||||
<SwiperSlide key={`slide_${index}`} id={`slide_${index}`} className={stylesS.customSlide}>
|
<SwiperSlide
|
||||||
|
key={`slide_${index}`}
|
||||||
|
id={`slide_${index}`}
|
||||||
|
className={stylesS.customSlide}
|
||||||
|
>
|
||||||
<CardSlide key={index} card={card} profile={profile} index={index} />
|
<CardSlide key={index} card={card} profile={profile} index={index} />
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
);
|
);
|
||||||
|
@@ -23,7 +23,7 @@ import { TalkCard } from '../components/TalkCard';
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
const Tab1 = () => {
|
const Tab1 = () => {
|
||||||
const pageRef = useRef();
|
const pageRef = useRef(null);
|
||||||
const talks = useStoreState(TalkStore, getTalks);
|
const talks = useStoreState(TalkStore, getTalks);
|
||||||
|
|
||||||
const router = useIonRouter();
|
const router = useIonRouter();
|
||||||
|
@@ -24,7 +24,7 @@ import { getFavourites, getSearchCount } from '../store/Selectors';
|
|||||||
|
|
||||||
const Tab1 = () => {
|
const Tab1 = () => {
|
||||||
const router = useIonRouter();
|
const router = useIonRouter();
|
||||||
const pageRef = useRef();
|
const pageRef = useRef(null);
|
||||||
const favourites = useStoreState(WordStore, getFavourites);
|
const favourites = useStoreState(WordStore, getFavourites);
|
||||||
const searchCount = useStoreState(WordStore, getSearchCount);
|
const searchCount = useStoreState(WordStore, getSearchCount);
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ import { WordStore } from '../store';
|
|||||||
import { searchWord } from '../utils';
|
import { searchWord } from '../utils';
|
||||||
|
|
||||||
const Tab2 = () => {
|
const Tab2 = () => {
|
||||||
const pageRef = useRef();
|
const pageRef = useRef(null);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [searchResult, setSearchResult] = useState(false);
|
const [searchResult, setSearchResult] = useState(false);
|
||||||
const [animatedClass, setAnimatedClass] = useState('');
|
const [animatedClass, setAnimatedClass] = useState('');
|
||||||
|
@@ -7,7 +7,7 @@ import { WordStore } from '../store';
|
|||||||
import { getFavourites } from '../store/Selectors';
|
import { getFavourites } from '../store/Selectors';
|
||||||
|
|
||||||
const Tab3 = () => {
|
const Tab3 = () => {
|
||||||
const pageRef = useRef();
|
const pageRef = useRef(null);
|
||||||
const favourites = useStoreState(WordStore, getFavourites);
|
const favourites = useStoreState(WordStore, getFavourites);
|
||||||
const [animatedClass, setAnimatedClass] = useState('animate__slideInLeft');
|
const [animatedClass, setAnimatedClass] = useState('animate__slideInLeft');
|
||||||
|
|
||||||
|
@@ -0,0 +1,72 @@
|
|||||||
|
.categoryPage ion-toolbar {
|
||||||
|
|
||||||
|
--border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCard {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
/* min-height: 20rem !important; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCardActions {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCardAction {
|
||||||
|
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCardHeader {
|
||||||
|
|
||||||
|
min-height: 17rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCardHeader p {
|
||||||
|
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent ion-button {
|
||||||
|
|
||||||
|
height: 1.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent p {
|
||||||
|
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCard img {
|
||||||
|
|
||||||
|
/* border-radius: 5px; */
|
||||||
|
/* padding: 1rem; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.productPrice {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
@@ -0,0 +1,105 @@
|
|||||||
|
import { IonButton, IonCard, IonCardContent, IonCardHeader, IonCol, IonIcon } from '@ionic/react';
|
||||||
|
import { arrowRedoOutline, cart, cartOutline, heart, heartOutline } from 'ionicons/icons';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { addToCart } from '../data/CartStore';
|
||||||
|
import { addToFavourites, FavouritesStore } from '../data/FavouritesStore';
|
||||||
|
import styles from './ProductCard.module.css';
|
||||||
|
|
||||||
|
const ProductCard = (props): React.FC => {
|
||||||
|
const { product, category, index, cartRef } = props;
|
||||||
|
const favourites = FavouritesStore.useState((s) => s.product_ids);
|
||||||
|
|
||||||
|
const productCartRef = useRef();
|
||||||
|
const productFavouriteRef = useRef();
|
||||||
|
const [isFavourite, setIsFavourite] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const tempIsFavourite = favourites.find((f) => f === `${category.slug}/${product.id}`);
|
||||||
|
setIsFavourite(tempIsFavourite ? true : false);
|
||||||
|
}, [props.product, favourites]);
|
||||||
|
|
||||||
|
const addProductToFavourites = (e, categorySlug, productID) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
addToFavourites(categorySlug, productID);
|
||||||
|
|
||||||
|
productFavouriteRef.current.style.display = '';
|
||||||
|
productFavouriteRef.current.classList.add('animate__fadeOutTopRight');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (productCartRef.current) {
|
||||||
|
productFavouriteRef.current.classList.remove('animate__fadeOutTopRight');
|
||||||
|
productFavouriteRef.current.style.display = 'none';
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addProductToCart = (e, categorySlug, productID) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
productCartRef.current.style.display = '';
|
||||||
|
productCartRef.current.classList.add('animate__fadeOutUp');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
cartRef.current.classList.add('animate__tada');
|
||||||
|
addToCart(categorySlug, productID);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
cartRef.current.classList.remove('animate__tada');
|
||||||
|
productCartRef.current.style.display = 'none';
|
||||||
|
}, 500);
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonCol size="6" key={`category_product_list_${index}`}>
|
||||||
|
<IonCard
|
||||||
|
routerLink={`/category/${category.slug}/${product.id}`}
|
||||||
|
className={styles.categoryCard}
|
||||||
|
>
|
||||||
|
<IonCardHeader className={styles.productCardHeader}>
|
||||||
|
<div className={styles.productCardActions}>
|
||||||
|
<IonIcon
|
||||||
|
className={styles.productCardAction}
|
||||||
|
color={isFavourite ? 'danger' : 'medium'}
|
||||||
|
icon={isFavourite ? heart : heartOutline}
|
||||||
|
onClick={(e) => addProductToFavourites(e, category.slug, product.id)}
|
||||||
|
/>
|
||||||
|
<IonIcon
|
||||||
|
ref={productFavouriteRef}
|
||||||
|
style={{ position: 'absolute', display: 'none' }}
|
||||||
|
className={`${styles.productCardAction} animate__animated`}
|
||||||
|
color="danger"
|
||||||
|
icon={heart}
|
||||||
|
/>
|
||||||
|
<IonIcon className={styles.productCardAction} size="medium" icon={arrowRedoOutline} />
|
||||||
|
</div>
|
||||||
|
<img src={product.image} alt="product pic" />
|
||||||
|
<p className="ion-text-wrap">{product.name}</p>
|
||||||
|
</IonCardHeader>
|
||||||
|
|
||||||
|
<IonCardContent className={styles.categoryCardContent}>
|
||||||
|
<div className={styles.productPrice}>
|
||||||
|
<IonButton style={{ width: '100%' }} color="light">
|
||||||
|
{product.price}
|
||||||
|
</IonButton>
|
||||||
|
<IonButton color="dark" onClick={(e) => addProductToCart(e, category.slug, product.id)}>
|
||||||
|
<IonIcon icon={cartOutline} />
|
||||||
|
</IonButton>
|
||||||
|
|
||||||
|
<IonIcon
|
||||||
|
ref={productCartRef}
|
||||||
|
icon={cart}
|
||||||
|
color="dark"
|
||||||
|
style={{ position: 'absolute', display: 'none', fontSize: '3rem' }}
|
||||||
|
className="animate__animated"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductCard;
|
@@ -0,0 +1,18 @@
|
|||||||
|
import { Store } from 'pullstate';
|
||||||
|
|
||||||
|
export const CartStore = new Store({
|
||||||
|
total: 0,
|
||||||
|
product_ids: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addToCart = (categorySlug, productID) => {
|
||||||
|
CartStore.update((s) => {
|
||||||
|
s.product_ids = [...s.product_ids, `${categorySlug}/${parseInt(productID)}`];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeFromCart = (productIndex) => {
|
||||||
|
CartStore.update((s) => {
|
||||||
|
s.product_ids.splice(productIndex, 1);
|
||||||
|
});
|
||||||
|
};
|
@@ -0,0 +1,17 @@
|
|||||||
|
import { Store } from "pullstate";
|
||||||
|
|
||||||
|
export const FavouritesStore = new Store({
|
||||||
|
|
||||||
|
total: 0,
|
||||||
|
product_ids: []
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addToFavourites = (categorySlug, productID) => {
|
||||||
|
FavouritesStore.update(s => {
|
||||||
|
if (s.product_ids.find(id => id === `${ categorySlug }/${ parseInt(productID) }`)) {
|
||||||
|
s.product_ids = s.product_ids.filter(id => id !== `${ categorySlug }/${ parseInt(productID) }`);
|
||||||
|
} else {
|
||||||
|
s.product_ids = [ ...s.product_ids, `${ categorySlug }/${ parseInt(productID) }` ];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
import { Store } from "pullstate";
|
||||||
|
|
||||||
|
export const ProductStore = new Store({
|
||||||
|
|
||||||
|
products: []
|
||||||
|
});
|
@@ -0,0 +1,57 @@
|
|||||||
|
import { ProductStore } from './ProductStore';
|
||||||
|
|
||||||
|
export const fetchData = async () => {
|
||||||
|
const json = [
|
||||||
|
'beds.json',
|
||||||
|
'armchairs.json',
|
||||||
|
'coffee_tables.json',
|
||||||
|
'cushions.json',
|
||||||
|
'floor_lamps.json',
|
||||||
|
'office_chairs.json',
|
||||||
|
];
|
||||||
|
|
||||||
|
var products = [];
|
||||||
|
|
||||||
|
json.forEach(async (category) => {
|
||||||
|
const products = await fetchProducts(category);
|
||||||
|
|
||||||
|
let categoryName = category.replace('.json', '');
|
||||||
|
categoryName = categoryName.replace('_', ' ');
|
||||||
|
categoryName = uppercaseWords(categoryName);
|
||||||
|
|
||||||
|
const productCategory = {
|
||||||
|
name: categoryName,
|
||||||
|
slug: category.replace('.json', ''),
|
||||||
|
cover: products[6].image,
|
||||||
|
products,
|
||||||
|
};
|
||||||
|
|
||||||
|
ProductStore.update((s) => {
|
||||||
|
s.products = [...s.products, productCategory];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return products;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchProducts = async (category) => {
|
||||||
|
const response = await fetch(`products/${category}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Set a product id
|
||||||
|
await data.forEach((d, i) => {
|
||||||
|
d.id = i + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const uppercaseWords = (words) => {
|
||||||
|
words = words
|
||||||
|
.toLowerCase()
|
||||||
|
.split(' ')
|
||||||
|
.map((s) => s.charAt(0).toUpperCase() + s.substring(1))
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
return words;
|
||||||
|
};
|
@@ -3,23 +3,48 @@ import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs }
|
|||||||
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
||||||
import { Route, Redirect } from 'react-router';
|
import { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
// import Tab1 from './AppPages/Tab1';
|
||||||
import Tab2 from './AppPages/Tab2';
|
// import Tab2 from './AppPages/Tab2';
|
||||||
|
|
||||||
|
import Home from './pages/Home';
|
||||||
|
import { fetchData } from './data/fetcher';
|
||||||
|
import CategoryProducts from './pages/CategoryProducts';
|
||||||
|
import Product from './pages/Product';
|
||||||
|
import FavouriteProducts from './pages/FavouriteProducts';
|
||||||
|
import CartProducts from './pages/CartProducts';
|
||||||
|
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
function DemoEcommerceExample(): React.JSX.Element {
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
function DemoEcommerceExample() {
|
|
||||||
return (
|
return (
|
||||||
<IonTabs>
|
<IonTabs>
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path="/demo-ecommerce-example/tab1">
|
<Route path="/demo-ecommerce-example/home" exact={true}>
|
||||||
<Tab1 />
|
<Home />
|
||||||
</Route>
|
|
||||||
<Route exact path="/demo-ecommerce-example/tab2">
|
|
||||||
<Tab2 />
|
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Redirect exact path="/demo-ecommerce-example" to="/demo-ecommerce-example/tab1" />
|
<Route path="/demo-ecommerce-example/favourites" exact>
|
||||||
|
<FavouriteProducts />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path="/demo-ecommerce-example/cart" exact>
|
||||||
|
<CartProducts />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path="/demo-ecommerce-example/category/:slug" exact>
|
||||||
|
<CategoryProducts />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path="/demo-ecommerce-example/category/:slug/:id" exact>
|
||||||
|
<Product />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Redirect exact path="/demo-ecommerce-example" to="/demo-ecommerce-example/home" />
|
||||||
</IonRouterOutlet>
|
</IonRouterOutlet>
|
||||||
|
|
||||||
{/* */}
|
{/* */}
|
||||||
|
4
03_source/mobile/src/pages/DemoEcommerceExample/module.d.ts
vendored
Normal file
4
03_source/mobile/src/pages/DemoEcommerceExample/module.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
declare module '*.module.css' {
|
||||||
|
const classes: { readonly [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
.cartCheckout {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cartFooter {
|
||||||
|
border-top: 2px solid rgb(200, 200, 200);
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cartCheckout ion-card-subtitle {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cartItem ion-avatar {
|
||||||
|
height: 4rem;
|
||||||
|
width: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cartSlider:not(:nth-child(1)) {
|
||||||
|
border-top: 2px solid rgb(236, 236, 236);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cartActions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
@@ -0,0 +1,161 @@
|
|||||||
|
import {
|
||||||
|
IonAvatar,
|
||||||
|
IonBadge,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCardSubtitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonImg,
|
||||||
|
IonItem,
|
||||||
|
IonItemOption,
|
||||||
|
IonItemOptions,
|
||||||
|
IonItemSliding,
|
||||||
|
IonLabel,
|
||||||
|
IonList,
|
||||||
|
IonNote,
|
||||||
|
IonPage,
|
||||||
|
IonRow,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { cart, checkmarkSharp, chevronBackOutline, trashOutline } from 'ionicons/icons';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { CartStore, removeFromCart } from '../data/CartStore';
|
||||||
|
import { ProductStore } from '../data/ProductStore';
|
||||||
|
|
||||||
|
import styles from './CartProducts.module.css';
|
||||||
|
|
||||||
|
const CartProducts = (): React.JSX.Element => {
|
||||||
|
const cartRef = useRef(null);
|
||||||
|
const products = ProductStore.useState((s) => s.products);
|
||||||
|
const shopCart = CartStore.useState((s) => s.product_ids);
|
||||||
|
const [cartProducts, setCartProducts] = useState([]);
|
||||||
|
const [amountLoaded, setAmountLoaded] = useState(6);
|
||||||
|
|
||||||
|
const [total, setTotal] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const getCartProducts = () => {
|
||||||
|
setCartProducts([]);
|
||||||
|
setTotal(0);
|
||||||
|
|
||||||
|
shopCart.forEach((product) => {
|
||||||
|
var favouriteParts = product.split('/');
|
||||||
|
var categorySlug = favouriteParts[0];
|
||||||
|
var productID = favouriteParts[1];
|
||||||
|
|
||||||
|
const tempCategory = products.filter((p) => p.slug === categorySlug)[0];
|
||||||
|
const tempProduct = tempCategory.products.filter(
|
||||||
|
(p) => parseInt(p.id) === parseInt(productID)
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const tempCartProduct = {
|
||||||
|
category: tempCategory,
|
||||||
|
product: tempProduct,
|
||||||
|
};
|
||||||
|
|
||||||
|
setTotal((prevTotal) => prevTotal + parseInt(tempProduct.price.replace('£', '')));
|
||||||
|
setCartProducts((prevSearchResults) => [...prevSearchResults, tempCartProduct]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getCartProducts();
|
||||||
|
}, [shopCart]);
|
||||||
|
|
||||||
|
const fetchMore = async (e) => {
|
||||||
|
// Increment the amount loaded by 6 for the next iteration
|
||||||
|
setAmountLoaded((prevAmount) => prevAmount + 6);
|
||||||
|
e.target.complete();
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeProductFromCart = async (index) => {
|
||||||
|
removeFromCart(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage id="category-page" className={styles.categoryPage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton color="dark" routerLink="/" routerDirection="back">
|
||||||
|
<IonIcon color="dark" icon={chevronBackOutline} />
|
||||||
|
Categories
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
<IonTitle>Cart</IonTitle>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonBadge color="dark">{shopCart.length}</IonBadge>
|
||||||
|
<IonButton color="dark">
|
||||||
|
<IonIcon ref={cartRef} className="animate__animated" icon={cart} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonRow className="ion-text-center ion-margin-top">
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonNote>
|
||||||
|
{cartProducts && cartProducts.length}{' '}
|
||||||
|
{cartProducts.length > 1 || cartProducts.length === 0 ? ' products' : ' product'}{' '}
|
||||||
|
found
|
||||||
|
</IonNote>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonList>
|
||||||
|
{cartProducts &&
|
||||||
|
cartProducts.map((product, index) => {
|
||||||
|
if (index <= amountLoaded) {
|
||||||
|
return (
|
||||||
|
<IonItemSliding className={styles.cartSlider}>
|
||||||
|
<IonItem key={index} lines="none" detail={false} className={styles.cartItem}>
|
||||||
|
<IonAvatar>
|
||||||
|
<IonImg src={product.product.image} />
|
||||||
|
</IonAvatar>
|
||||||
|
<IonLabel className="ion-padding-start ion-text-wrap">
|
||||||
|
<p>{product.category.name}</p>
|
||||||
|
<h4>{product.product.name}</h4>
|
||||||
|
</IonLabel>
|
||||||
|
|
||||||
|
<div className={styles.cartActions}>
|
||||||
|
<IonBadge color="dark">{product.product.price}</IonBadge>
|
||||||
|
</div>
|
||||||
|
</IonItem>
|
||||||
|
|
||||||
|
<IonItemOptions side="end">
|
||||||
|
<IonItemOption
|
||||||
|
color="danger"
|
||||||
|
style={{ paddingLeft: '1rem', paddingRight: '1rem' }}
|
||||||
|
onClick={() => removeProductFromCart(index)}
|
||||||
|
>
|
||||||
|
<IonIcon icon={trashOutline} />
|
||||||
|
</IonItemOption>
|
||||||
|
</IonItemOptions>
|
||||||
|
</IonItemSliding>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</IonList>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter className={styles.cartFooter}>
|
||||||
|
<div className={styles.cartCheckout}>
|
||||||
|
<IonCardSubtitle>£{total.toFixed(2)}</IonCardSubtitle>
|
||||||
|
|
||||||
|
<IonButton color="dark">
|
||||||
|
<IonIcon icon={checkmarkSharp} />
|
||||||
|
Checkout
|
||||||
|
</IonButton>
|
||||||
|
</div>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CartProducts;
|
@@ -0,0 +1,10 @@
|
|||||||
|
.categoryPage ion-toolbar {
|
||||||
|
|
||||||
|
--border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
|
||||||
|
--background: rgb(240, 240, 240);
|
||||||
|
--color: black;
|
||||||
|
}
|
@@ -0,0 +1,134 @@
|
|||||||
|
import {
|
||||||
|
IonBadge,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonInfiniteScroll,
|
||||||
|
IonInfiniteScrollContent,
|
||||||
|
IonNote,
|
||||||
|
IonPage,
|
||||||
|
IonRow,
|
||||||
|
IonSearchbar,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { cart, chevronBackOutline, searchOutline } from 'ionicons/icons';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
import ProductCard from '../components/ProductCard';
|
||||||
|
|
||||||
|
import { CartStore } from '../data/CartStore';
|
||||||
|
import { ProductStore } from '../data/ProductStore';
|
||||||
|
|
||||||
|
import styles from './CategoryProducts.module.css';
|
||||||
|
|
||||||
|
const CategoryProducts = () => {
|
||||||
|
const params = useParams();
|
||||||
|
const cartRef = useRef();
|
||||||
|
const products = ProductStore.useState((s) => s.products);
|
||||||
|
const shopCart = CartStore.useState((s) => s.product_ids);
|
||||||
|
const [category, setCategory] = useState({});
|
||||||
|
const [searchResults, setsearchResults] = useState([]);
|
||||||
|
const [amountLoaded, setAmountLoaded] = useState(6);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const categorySlug = params.slug;
|
||||||
|
const tempCategory = products.filter((p) => p.slug === categorySlug)[0];
|
||||||
|
setCategory(tempCategory);
|
||||||
|
setsearchResults(tempCategory.products);
|
||||||
|
}, [params.slug]);
|
||||||
|
|
||||||
|
const fetchMore = async (e) => {
|
||||||
|
// Increment the amount loaded by 6 for the next iteration
|
||||||
|
setAmountLoaded((prevAmount) => prevAmount + 6);
|
||||||
|
e.target.complete();
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = async (e) => {
|
||||||
|
const searchVal = e.target.value;
|
||||||
|
|
||||||
|
if (searchVal !== '') {
|
||||||
|
const tempResults = category.products.filter((p) =>
|
||||||
|
p.name.toLowerCase().includes(searchVal.toLowerCase())
|
||||||
|
);
|
||||||
|
setsearchResults(tempResults);
|
||||||
|
} else {
|
||||||
|
setsearchResults(category.products);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage id="category-page" className={styles.categoryPage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton color="dark" text={category.name} routerLink="/" routerDirection="back">
|
||||||
|
<IonIcon color="dark" icon={chevronBackOutline} />
|
||||||
|
Categories
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
<IonTitle>{category && category.name}</IonTitle>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonBadge color="dark">{shopCart.length}</IonBadge>
|
||||||
|
<IonButton color="dark" routerLink="/cart">
|
||||||
|
<IonIcon ref={cartRef} className="animate__animated" icon={cart} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonSearchbar
|
||||||
|
className={styles.search}
|
||||||
|
onKeyUp={search}
|
||||||
|
placeholder="Try 'high back'"
|
||||||
|
searchIcon={searchOutline}
|
||||||
|
animated={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow className="ion-text-center">
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonNote>
|
||||||
|
{searchResults && searchResults.length}{' '}
|
||||||
|
{searchResults.length > 1 || searchResults.length === 0 ? ' products' : ' product'}{' '}
|
||||||
|
found
|
||||||
|
</IonNote>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow>
|
||||||
|
{searchResults &&
|
||||||
|
searchResults.map((product, index) => {
|
||||||
|
if (index <= amountLoaded && product.image) {
|
||||||
|
return (
|
||||||
|
<ProductCard
|
||||||
|
key={`category_product_${index}`}
|
||||||
|
product={product}
|
||||||
|
index={index}
|
||||||
|
cartRef={cartRef}
|
||||||
|
category={category}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
|
||||||
|
<IonInfiniteScroll threshold="100px" onIonInfinite={fetchMore}>
|
||||||
|
<IonInfiniteScrollContent
|
||||||
|
loadingSpinner="bubbles"
|
||||||
|
loadingText="Fetching more..."
|
||||||
|
></IonInfiniteScrollContent>
|
||||||
|
</IonInfiniteScroll>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CategoryProducts;
|
@@ -0,0 +1,131 @@
|
|||||||
|
import {
|
||||||
|
IonBadge,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonInfiniteScroll,
|
||||||
|
IonInfiniteScrollContent,
|
||||||
|
IonNote,
|
||||||
|
IonPage,
|
||||||
|
IonRow,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { cart, chevronBackOutline } from 'ionicons/icons';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import ProductCard from '../components/ProductCard';
|
||||||
|
import { CartStore } from '../data/CartStore';
|
||||||
|
import { FavouritesStore } from '../data/FavouritesStore';
|
||||||
|
import { ProductStore } from '../data/ProductStore';
|
||||||
|
|
||||||
|
import styles from './CategoryProducts.module.css';
|
||||||
|
|
||||||
|
const FavouriteProducts = () => {
|
||||||
|
const cartRef = useRef(null);
|
||||||
|
const products = ProductStore.useState((s) => s.products);
|
||||||
|
const favourites = FavouritesStore.useState((s) => s.product_ids);
|
||||||
|
const shopCart = CartStore.useState((s) => s.product_ids);
|
||||||
|
const [searchResults, setSearchResults] = useState([]);
|
||||||
|
const [amountLoaded, setAmountLoaded] = useState(6);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const getFavourites = () => {
|
||||||
|
setSearchResults([]);
|
||||||
|
|
||||||
|
favourites.forEach((favourite) => {
|
||||||
|
var favouriteParts = favourite.split('/');
|
||||||
|
var categorySlug = favouriteParts[0];
|
||||||
|
var productID = favouriteParts[1];
|
||||||
|
|
||||||
|
const tempCategory = products.filter((p) => p.slug === categorySlug)[0];
|
||||||
|
const tempProduct = tempCategory.products.filter(
|
||||||
|
(p) => parseInt(p.id) === parseInt(productID)
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const tempFavourite = {
|
||||||
|
category: tempCategory,
|
||||||
|
product: tempProduct,
|
||||||
|
};
|
||||||
|
|
||||||
|
setSearchResults((prevSearchResults) => [...prevSearchResults, tempFavourite]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getFavourites();
|
||||||
|
}, [favourites]);
|
||||||
|
|
||||||
|
const fetchMore = async (e) => {
|
||||||
|
// Increment the amount loaded by 6 for the next iteration
|
||||||
|
setAmountLoaded((prevAmount) => prevAmount + 6);
|
||||||
|
e.target.complete();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage id="category-page" className={styles.categoryPage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton color="dark" routerLink="/" routerDirection="back">
|
||||||
|
<IonIcon color="dark" icon={chevronBackOutline} />
|
||||||
|
Categories
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
<IonTitle>Favourites</IonTitle>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonBadge color="dark">{shopCart.length}</IonBadge>
|
||||||
|
<IonButton color="dark">
|
||||||
|
<IonIcon ref={cartRef} className="animate__animated" icon={cart} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow className="ion-text-center">
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonNote>
|
||||||
|
{searchResults && searchResults.length}{' '}
|
||||||
|
{searchResults.length > 1 || searchResults.length === 0
|
||||||
|
? ' favourites'
|
||||||
|
: ' favourite'}{' '}
|
||||||
|
found
|
||||||
|
</IonNote>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow>
|
||||||
|
{searchResults &&
|
||||||
|
searchResults.map((product, index) => {
|
||||||
|
if (index <= amountLoaded) {
|
||||||
|
return (
|
||||||
|
<ProductCard
|
||||||
|
key={`category_product_${index}`}
|
||||||
|
product={product.product}
|
||||||
|
index={index}
|
||||||
|
cartRef={cartRef}
|
||||||
|
category={product.category}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
|
||||||
|
<IonInfiniteScroll threshold="100px" onIonInfinite={fetchMore}>
|
||||||
|
<IonInfiniteScrollContent
|
||||||
|
loadingSpinner="bubbles"
|
||||||
|
loadingText="Fetching more..."
|
||||||
|
></IonInfiniteScrollContent>
|
||||||
|
</IonInfiniteScroll>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FavouriteProducts;
|
@@ -0,0 +1,42 @@
|
|||||||
|
.homePage ion-toolbar {
|
||||||
|
|
||||||
|
--border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
color: var(--ion-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCard,
|
||||||
|
.categoryCardContent {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent ion-button {
|
||||||
|
|
||||||
|
height: 1.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent {
|
||||||
|
|
||||||
|
background-color: rgb(238, 238, 238);
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent ion-card-subtitle {
|
||||||
|
|
||||||
|
/* color: rgb(78, 78, 78); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCard img {
|
||||||
|
|
||||||
|
/* border-radius: 5px; */
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
@@ -0,0 +1,88 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import {
|
||||||
|
IonBadge,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCard,
|
||||||
|
IonCardContent,
|
||||||
|
IonCardSubtitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonPage,
|
||||||
|
IonRow,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
|
||||||
|
import styles from './Home.module.css';
|
||||||
|
import { cart, heart } from 'ionicons/icons';
|
||||||
|
|
||||||
|
import { ProductStore } from '../data/ProductStore';
|
||||||
|
import { FavouritesStore } from '../data/FavouritesStore';
|
||||||
|
import { CartStore } from '../data/CartStore';
|
||||||
|
|
||||||
|
const Home = () => {
|
||||||
|
const products = ProductStore.useState((s) => s.products);
|
||||||
|
const favourites = FavouritesStore.useState((s) => s.product_ids);
|
||||||
|
const shopCart = CartStore.useState((s) => s.product_ids);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage id="home-page" className={styles.homePage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle>Categories</IonTitle>
|
||||||
|
|
||||||
|
<IonButtons slot="start" className="ion-padding-start">
|
||||||
|
<IonCardSubtitle className={styles.logo}>Ionic Furniture</IonCardSubtitle>
|
||||||
|
</IonButtons>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonBadge color="danger">{favourites.length}</IonBadge>
|
||||||
|
<IonButton color="danger" routerLink="/favourites">
|
||||||
|
<IonIcon icon={heart} />
|
||||||
|
</IonButton>
|
||||||
|
|
||||||
|
<IonBadge color="dark">{shopCart.length}</IonBadge>
|
||||||
|
<IonButton color="dark" routerLink="/cart">
|
||||||
|
<IonIcon icon={cart} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonHeader collapse="condense">
|
||||||
|
<IonToolbar>
|
||||||
|
<IonTitle size="large">Categories</IonTitle>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow>
|
||||||
|
{products.map((category, index) => {
|
||||||
|
return (
|
||||||
|
<IonCol size="6" key={`category_list_${index}`}>
|
||||||
|
<IonCard
|
||||||
|
routerLink={`/category/${category.slug}`}
|
||||||
|
className={styles.categoryCard}
|
||||||
|
>
|
||||||
|
<img src={category.cover} alt="category cover" />
|
||||||
|
|
||||||
|
<IonCardContent className={styles.categoryCardContent}>
|
||||||
|
<IonCardSubtitle>{category.name}</IonCardSubtitle>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
@@ -0,0 +1,66 @@
|
|||||||
|
.categoryPage ion-toolbar {
|
||||||
|
|
||||||
|
--border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCard {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCardActions {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCardAction {
|
||||||
|
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCardHeader {
|
||||||
|
|
||||||
|
min-height: 17rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productCardHeader p {
|
||||||
|
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent ion-button {
|
||||||
|
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryCardContent p {
|
||||||
|
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.productPrice {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
@@ -0,0 +1,210 @@
|
|||||||
|
import {
|
||||||
|
IonBadge,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCard,
|
||||||
|
IonCardContent,
|
||||||
|
IonCardHeader,
|
||||||
|
IonCardSubtitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonPage,
|
||||||
|
IonRow,
|
||||||
|
IonTitle,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import {
|
||||||
|
arrowRedoOutline,
|
||||||
|
cart,
|
||||||
|
cartOutline,
|
||||||
|
chevronBackOutline,
|
||||||
|
heart,
|
||||||
|
heartOutline,
|
||||||
|
} from 'ionicons/icons';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
import ProductCard from '../components/ProductCard';
|
||||||
|
import { addToCart, CartStore } from '../data/CartStore';
|
||||||
|
import { addToFavourites, FavouritesStore } from '../data/FavouritesStore';
|
||||||
|
import { ProductStore } from '../data/ProductStore';
|
||||||
|
|
||||||
|
import styles from './Product.module.css';
|
||||||
|
|
||||||
|
const Product = () => {
|
||||||
|
const params = useParams();
|
||||||
|
const cartRef = useRef(null);
|
||||||
|
const products = ProductStore.useState((s) => s.products);
|
||||||
|
const favourites = FavouritesStore.useState((s) => s.product_ids);
|
||||||
|
const [isFavourite, setIsFavourite] = useState(false);
|
||||||
|
const shopCart = CartStore.useState((s) => s.product_ids);
|
||||||
|
const [product, setProduct] = useState({});
|
||||||
|
const [category, setCategory] = useState({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const categorySlug = params.slug;
|
||||||
|
const productID = params.id;
|
||||||
|
const tempCategory = products.filter((p) => p.slug === categorySlug)[0];
|
||||||
|
const tempProduct = tempCategory.products.filter(
|
||||||
|
(p) => parseInt(p.id) === parseInt(productID)
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const tempIsFavourite = favourites.find((f) => f === `${categorySlug}/${productID}`);
|
||||||
|
|
||||||
|
setIsFavourite(tempIsFavourite);
|
||||||
|
setCategory(tempCategory);
|
||||||
|
setProduct(tempProduct);
|
||||||
|
}, [params.slug, params.id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const tempIsFavourite = favourites.find((f) => f === `${category.slug}/${product.id}`);
|
||||||
|
setIsFavourite(tempIsFavourite ? true : false);
|
||||||
|
}, [favourites, product]);
|
||||||
|
|
||||||
|
const addProductToFavourites = (e, categorySlug, productID) => {
|
||||||
|
e.preventDefault();
|
||||||
|
addToFavourites(categorySlug, productID);
|
||||||
|
|
||||||
|
document.getElementById(
|
||||||
|
`placeholder_favourite_product_${categorySlug}_${productID}`
|
||||||
|
).style.display = '';
|
||||||
|
document
|
||||||
|
.getElementById(`placeholder_favourite_product_${categorySlug}_${productID}`)
|
||||||
|
.classList.add('animate__fadeOutTopRight');
|
||||||
|
};
|
||||||
|
|
||||||
|
const addProductToCart = (e, categorySlug, productID) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
document.getElementById(`placeholder_cart_${categorySlug}_${productID}`).style.display = '';
|
||||||
|
document
|
||||||
|
.getElementById(`placeholder_cart_${categorySlug}_${productID}`)
|
||||||
|
.classList.add('animate__fadeOutUp');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
cartRef.current.classList.add('animate__tada');
|
||||||
|
addToCart(categorySlug, productID);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
cartRef.current.classList.remove('animate__tada');
|
||||||
|
}, 500);
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage id="category-page" className={styles.categoryPage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton
|
||||||
|
color="dark"
|
||||||
|
text={category.name}
|
||||||
|
routerLink={`/category/${category.slug}`}
|
||||||
|
routerDirection="back"
|
||||||
|
>
|
||||||
|
<IonIcon color="dark" icon={chevronBackOutline} />
|
||||||
|
{category.name}
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
|
||||||
|
<IonTitle>View Product</IonTitle>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonBadge color="dark">{shopCart.length}</IonBadge>
|
||||||
|
<IonButton color="dark" routerLink="/cart">
|
||||||
|
<IonIcon ref={cartRef} className="animate__animated" icon={cart} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonCard className={styles.categoryCard}>
|
||||||
|
<IonCardHeader className={styles.productCardHeader}>
|
||||||
|
<div className={styles.productCardActions}>
|
||||||
|
<IonIcon
|
||||||
|
className={styles.productCardAction}
|
||||||
|
color={isFavourite ? 'danger' : 'medium'}
|
||||||
|
icon={isFavourite ? heart : heartOutline}
|
||||||
|
onClick={(e) => addProductToFavourites(e, category.slug, product.id)}
|
||||||
|
/>
|
||||||
|
<IonIcon
|
||||||
|
style={{ position: 'absolute', display: 'none' }}
|
||||||
|
id={`placeholder_favourite_product_${category.slug}_${product.id}`}
|
||||||
|
className={`${styles.productCardAction} animate__animated`}
|
||||||
|
color="danger"
|
||||||
|
icon={heart}
|
||||||
|
/>
|
||||||
|
<IonIcon
|
||||||
|
className={styles.productCardAction}
|
||||||
|
size="medium"
|
||||||
|
icon={arrowRedoOutline}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<img src={product.image} alt="product pic" />
|
||||||
|
<p className="ion-text-wrap">{product.name}</p>
|
||||||
|
</IonCardHeader>
|
||||||
|
|
||||||
|
<IonCardContent className={styles.categoryCardContent}>
|
||||||
|
<div className={styles.productPrice}>
|
||||||
|
<IonButton color="light" size="large">
|
||||||
|
{product.price}
|
||||||
|
</IonButton>
|
||||||
|
<IonButton
|
||||||
|
size="large"
|
||||||
|
color="dark"
|
||||||
|
onClick={(e) => addProductToCart(e, category.slug, product.id)}
|
||||||
|
>
|
||||||
|
<IonIcon icon={cartOutline} />
|
||||||
|
Add to Cart
|
||||||
|
</IonButton>
|
||||||
|
|
||||||
|
<IonIcon
|
||||||
|
icon={cart}
|
||||||
|
color="dark"
|
||||||
|
style={{ position: 'absolute', display: 'none', fontSize: '3rem' }}
|
||||||
|
id={`placeholder_cart_${category.slug}_${product.id}`}
|
||||||
|
className="animate__animated"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</IonCardContent>
|
||||||
|
</IonCard>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className="ion-text-center">
|
||||||
|
<IonCol size="12">
|
||||||
|
<IonCardSubtitle>Similar products...</IonCardSubtitle>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow>
|
||||||
|
{category &&
|
||||||
|
category.products &&
|
||||||
|
category.products.map((similar, index) => {
|
||||||
|
if (similar.id !== product.id && product.image && index < 4) {
|
||||||
|
return (
|
||||||
|
<ProductCard
|
||||||
|
key={`similar_product_${index}`}
|
||||||
|
product={similar}
|
||||||
|
index={index}
|
||||||
|
isFavourite={false}
|
||||||
|
cartRef={cartRef}
|
||||||
|
category={category}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Product;
|
@@ -0,0 +1,81 @@
|
|||||||
|
/* Ionic Variables and Theming. For more info, please see:
|
||||||
|
http://ionicframework.com/docs/theming/ */
|
||||||
|
|
||||||
|
/** Ionic CSS Variables **/
|
||||||
|
:root {
|
||||||
|
/** primary **/
|
||||||
|
--ion-color-primary: #3880ff;
|
||||||
|
--ion-color-primary-rgb: 56, 128, 255;
|
||||||
|
--ion-color-primary-contrast: #ffffff;
|
||||||
|
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-primary-shade: #3171e0;
|
||||||
|
--ion-color-primary-tint: #4c8dff;
|
||||||
|
|
||||||
|
/** secondary **/
|
||||||
|
--ion-color-secondary: #3dc2ff;
|
||||||
|
--ion-color-secondary-rgb: 61, 194, 255;
|
||||||
|
--ion-color-secondary-contrast: #ffffff;
|
||||||
|
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-secondary-shade: #36abe0;
|
||||||
|
--ion-color-secondary-tint: #50c8ff;
|
||||||
|
|
||||||
|
/** tertiary **/
|
||||||
|
--ion-color-tertiary: #5260ff;
|
||||||
|
--ion-color-tertiary-rgb: 82, 96, 255;
|
||||||
|
--ion-color-tertiary-contrast: #ffffff;
|
||||||
|
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-tertiary-shade: #4854e0;
|
||||||
|
--ion-color-tertiary-tint: #6370ff;
|
||||||
|
|
||||||
|
/** success **/
|
||||||
|
--ion-color-success: #2dd36f;
|
||||||
|
--ion-color-success-rgb: 45, 211, 111;
|
||||||
|
--ion-color-success-contrast: #ffffff;
|
||||||
|
--ion-color-success-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-success-shade: #28ba62;
|
||||||
|
--ion-color-success-tint: #42d77d;
|
||||||
|
|
||||||
|
/** warning **/
|
||||||
|
--ion-color-warning: #ffc409;
|
||||||
|
--ion-color-warning-rgb: 255, 196, 9;
|
||||||
|
--ion-color-warning-contrast: #000000;
|
||||||
|
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-warning-shade: #e0ac08;
|
||||||
|
--ion-color-warning-tint: #ffca22;
|
||||||
|
|
||||||
|
/** danger **/
|
||||||
|
--ion-color-danger: #eb445a;
|
||||||
|
--ion-color-danger-rgb: 235, 68, 90;
|
||||||
|
--ion-color-danger-contrast: #ffffff;
|
||||||
|
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-danger-shade: #cf3c4f;
|
||||||
|
--ion-color-danger-tint: #ed576b;
|
||||||
|
|
||||||
|
/** dark **/
|
||||||
|
--ion-color-dark: #222428;
|
||||||
|
--ion-color-dark-rgb: 34, 36, 40;
|
||||||
|
--ion-color-dark-contrast: #ffffff;
|
||||||
|
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-dark-shade: #1e2023;
|
||||||
|
--ion-color-dark-tint: #383a3e;
|
||||||
|
|
||||||
|
/** medium **/
|
||||||
|
--ion-color-medium: #92949c;
|
||||||
|
--ion-color-medium-rgb: 146, 148, 156;
|
||||||
|
--ion-color-medium-contrast: #ffffff;
|
||||||
|
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-medium-shade: #808289;
|
||||||
|
--ion-color-medium-tint: #9d9fa6;
|
||||||
|
|
||||||
|
/** light **/
|
||||||
|
--ion-color-light: #f4f5f8;
|
||||||
|
--ion-color-light-rgb: 244, 245, 248;
|
||||||
|
--ion-color-light-contrast: #000000;
|
||||||
|
--ion-color-light-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-light-shade: #d7d8da;
|
||||||
|
--ion-color-light-tint: #f5f6f9;
|
||||||
|
|
||||||
|
--ion-toolbar-color: black;
|
||||||
|
--ion-grid-column-padding: 0rem;
|
||||||
|
/* --ion-toolbar-background: var(--ion-color-warning); */
|
||||||
|
}
|
@@ -2,3 +2,8 @@ declare module '*.module.css' {
|
|||||||
const classes: { readonly [key: string]: string };
|
const classes: { readonly [key: string]: string };
|
||||||
export default classes;
|
export default classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '*.module.scss' {
|
||||||
|
const classes: { readonly [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
@@ -56,6 +56,12 @@ const Home = () => {
|
|||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonTitle>Popular</IonTitle>
|
<IonTitle>Popular</IonTitle>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
const router = useIonRouter();
|
||||||
|
function handleBackClick() {
|
||||||
|
router.goBack();
|
||||||
|
}
|
||||||
|
*/}
|
||||||
<IonButtons slot="start">
|
<IonButtons slot="start">
|
||||||
<IonButton onClick={() => handleBackClick()}>
|
<IonButton onClick={() => handleBackClick()}>
|
||||||
<IonIcon icon={chevronBackOutline} color="primary" />
|
<IonIcon icon={chevronBackOutline} color="primary" />
|
||||||
|
@@ -49,42 +49,67 @@ import {
|
|||||||
apps,
|
apps,
|
||||||
appsOutline,
|
appsOutline,
|
||||||
book,
|
book,
|
||||||
|
bookOutline,
|
||||||
|
brushOutline,
|
||||||
|
calculatorOutline,
|
||||||
car,
|
car,
|
||||||
cart,
|
cart,
|
||||||
|
cartOutline,
|
||||||
cashOutline,
|
cashOutline,
|
||||||
chatbubbleEllipses,
|
chatbubbleEllipses,
|
||||||
|
chatbubbleEllipsesOutline,
|
||||||
chatbubbleOutline,
|
chatbubbleOutline,
|
||||||
chevronBackOutline,
|
chevronBackOutline,
|
||||||
chevronForward,
|
chevronForward,
|
||||||
chevronForwardOutline,
|
chevronForwardOutline,
|
||||||
|
cloudOutline,
|
||||||
|
codeSlashOutline,
|
||||||
|
codeWorkingOutline,
|
||||||
colorPaletteOutline,
|
colorPaletteOutline,
|
||||||
createOutline,
|
createOutline,
|
||||||
document,
|
document,
|
||||||
documentTextOutline,
|
documentTextOutline,
|
||||||
|
filmOutline,
|
||||||
|
flashOutline,
|
||||||
gift,
|
gift,
|
||||||
giftOutline,
|
giftOutline,
|
||||||
globeSharp,
|
globeSharp,
|
||||||
|
gridOutline,
|
||||||
heart,
|
heart,
|
||||||
|
imageOutline,
|
||||||
|
imagesOutline,
|
||||||
keyOutline,
|
keyOutline,
|
||||||
languageOutline,
|
languageOutline,
|
||||||
layers,
|
layers,
|
||||||
|
layersOutline,
|
||||||
list,
|
list,
|
||||||
listCircle,
|
listCircle,
|
||||||
|
listOutline,
|
||||||
|
logInOutline,
|
||||||
|
logoFacebook,
|
||||||
mapOutline,
|
mapOutline,
|
||||||
menuOutline,
|
menuOutline,
|
||||||
|
paperPlaneOutline,
|
||||||
people,
|
people,
|
||||||
person,
|
person,
|
||||||
|
personCircleOutline,
|
||||||
|
personOutline,
|
||||||
|
pizzaOutline,
|
||||||
|
qrCodeOutline,
|
||||||
|
refreshOutline,
|
||||||
restaurant,
|
restaurant,
|
||||||
|
restaurantOutline,
|
||||||
settingsOutline,
|
settingsOutline,
|
||||||
shareSocialOutline,
|
shareSocialOutline,
|
||||||
statsChart,
|
statsChart,
|
||||||
sunny,
|
sunny,
|
||||||
swapHorizontal,
|
swapHorizontal,
|
||||||
trashOutline,
|
trashOutline,
|
||||||
|
walkOutline,
|
||||||
} from 'ionicons/icons';
|
} from 'ionicons/icons';
|
||||||
import AboutPopover from '../../components/AboutPopover';
|
import AboutPopover from '../../components/AboutPopover';
|
||||||
import { OverlayEventDetail } from '@ionic/react/dist/types/components/react-component-lib/interfaces';
|
import { OverlayEventDetail } from '@ionic/react/dist/types/components/react-component-lib/interfaces';
|
||||||
import paths from '../../paths';
|
import PATHS from '../../PATHS';
|
||||||
import { logoutUser, setAccessToken, setIsLoggedIn } from '../../data/user/user.actions';
|
import { logoutUser, setAccessToken, setIsLoggedIn } from '../../data/user/user.actions';
|
||||||
|
|
||||||
interface OwnProps {}
|
interface OwnProps {}
|
||||||
@@ -102,7 +127,13 @@ interface DispatchProps {
|
|||||||
|
|
||||||
interface SettingsProps extends OwnProps, StateProps, DispatchProps {}
|
interface SettingsProps extends OwnProps, StateProps, DispatchProps {}
|
||||||
|
|
||||||
const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logoutUser, setAccessToken, setIsLoggedIn }) => {
|
const DemoList: React.FC<SettingsProps> = ({
|
||||||
|
speakers,
|
||||||
|
speakerSessions,
|
||||||
|
logoutUser,
|
||||||
|
setAccessToken,
|
||||||
|
setIsLoggedIn,
|
||||||
|
}) => {
|
||||||
const [events, setEvents] = useState<Event[] | []>([]);
|
const [events, setEvents] = useState<Event[] | []>([]);
|
||||||
const [showPopover, setShowPopover] = useState(false);
|
const [showPopover, setShowPopover] = useState(false);
|
||||||
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
|
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
|
||||||
@@ -122,23 +153,23 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleLanguageClick() {
|
function handleLanguageClick() {
|
||||||
router.push(paths.CHANGE_LANGUAGE);
|
router.push(PATHS.CHANGE_LANGUAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNotImplementedClick() {
|
function handleNotImplementedClick() {
|
||||||
router.push(paths.NOT_IMPLEMENTED);
|
router.push(PATHS.NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDemoPageClick() {
|
function handleDemoPageClick() {
|
||||||
router.push(paths.DEMO_PAGE);
|
router.push(PATHS.DEMO_PAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleServiceAgreementClick() {
|
function handleServiceAgreementClick() {
|
||||||
router.push(paths.SERVICE_AGREEMENT);
|
router.push(PATHS.SERVICE_AGREEMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePrivacyAgreementClick() {
|
function handlePrivacyAgreementClick() {
|
||||||
router.push(paths.PRIVACY_AGREEMENT);
|
router.push(PATHS.PRIVACY_AGREEMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [showLogoutConfirmModal, setShowLogoutConfirmModal] = useState<boolean>(false);
|
const [showLogoutConfirmModal, setShowLogoutConfirmModal] = useState<boolean>(false);
|
||||||
@@ -158,12 +189,8 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
setShowLogoutConfirmModal(false);
|
setShowLogoutConfirmModal(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDemoWeatherApp() {
|
|
||||||
router.push(paths.DEMO_WEATHER_APP);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDemoReactShopClick() {
|
function handleDemoReactShopClick() {
|
||||||
router.push(paths.DEMO_REACT_SHOP);
|
router.push(PATHS.DEMO_REACT_SHOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -192,263 +219,29 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonHeader>
|
</IonHeader>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => handleDemoWeatherApp()}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_WEATHER_APP_UI)}>
|
||||||
<IonIcon slot="start" icon={sunny} size="large"></IonIcon>
|
<IonIcon slot="start" icon={sunny} size="large"></IonIcon>
|
||||||
<IonLabel>Weather App</IonLabel>
|
<IonLabel>Weather App</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
{/* */}
|
|
||||||
{/* */}
|
|
||||||
{/* */}
|
|
||||||
{/* */}
|
|
||||||
{/* */}
|
|
||||||
{/* */}
|
|
||||||
{/* */}
|
|
||||||
{/* */}
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_ACCORDION_TUTORIAL, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_2FA_EXAMPLE, 'forward')}>
|
||||||
<IonIcon slot="start" icon={list} size="large"></IonIcon>
|
<IonIcon slot="start" icon={keyOutline} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Accordion Tutorial</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_BANKING_UI, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={cashOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>
|
<IonLabel>
|
||||||
Demo Banking UI <span style={{ fontWeight: 'bold' }}>(in the middle, style outstanding)</span>
|
Demo 2FA Example{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>layout only, not functioning</span>
|
||||||
</IonLabel>
|
</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL, 'forward')}>
|
<IonItem
|
||||||
<IonIcon slot="start" icon={mapOutline} size="large"></IonIcon>
|
button={true}
|
||||||
<IonLabel>
|
onClick={() => router.push(PATHS.DEMO_REACT_THEME_SWITCHER, 'forward')}
|
||||||
Demo Capacitor Google Maps Tutorial <span style={{ fontWeight: 'bold' }}>need a google map api key</span>
|
>
|
||||||
</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_COLOR_TUTORIAL, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={colorPaletteOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Color Tutorial</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
{/*
|
|
||||||
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_ECOMMERCE_EXAMPLE, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Ecommerce Example</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_FACEBOOK_CLONE, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={logoFacebook} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Facebook Clone</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_FAST_FOOD_APP, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={restaurantOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Fast Food App</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_FLOATING_TABS, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={layersOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Floating Tabs</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_INSTAGRAM_CLONE, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={imageOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Instagram Clone</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_KANBAN_BOARD, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={gridOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Kanban Board</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_ORDERING_APP, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={pizzaOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Ordering App</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_PROFILE_EXAMPLE, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={personOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Profile Example</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_PULLSTATE_TUTORIAL, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={codeWorkingOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo Pullstate Tutorial</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_ADD_TO_CART, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Add to Cart</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_CALCULATOR, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={calculatorOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Calculator</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_DRAWING_CANVAS, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={brushOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Drawing Canvas</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_HOOK_FORM_EXAMPLE, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={codeSlashOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Hook Form Example</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_ITEM_LIST, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={listOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Item List</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_LIFECYCLES, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={refreshOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Lifecycles</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_LOGIN, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={logInOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Login</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_MARVEL_APP, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={flashOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Marvel App</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_MOVIE_APP_WITH_ALGOLIA, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={filmOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Movie App with Algolia</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_NOTES, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={documentTextOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Notes</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_ONBOARDING_UI, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={walkOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Onboarding UI</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_PROFILE_DASHBOARD_UI, 'forward')):
|
|
||||||
<IonIcon slot="start" icon={personCircleOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Profile Dashboard UI</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_QR_CODE, 'forward')}>
|
|
||||||
<IonIcon slot="start" icon={qrCodeOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React QR Code</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_QUOTES, 'forward')):
|
|
||||||
<IonIcon slot="start" icon={bookOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Quotes</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_SHOP_UI, 'forward')):
|
|
||||||
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Shop UI</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_TABS_MENUS_CUSTOM, 'forward')):
|
|
||||||
<IonIcon slot="start" icon={layersOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React Tabs Menus Custom</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_THEME_SWITCHER, 'forward')):
|
|
||||||
<IonIcon slot="start" icon={colorPaletteOutline} size="large"></IonIcon>
|
<IonIcon slot="start" icon={colorPaletteOutline} size="large"></IonIcon>
|
||||||
<IonLabel>Demo React Theme Switcher</IonLabel>
|
<IonLabel>Demo React Theme Switcher</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -456,15 +249,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_WHATSAPP_CLONE, 'forward')):
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_SKELETON_TEXT, 'forward')}>
|
||||||
<IonIcon slot="start" icon={chatbubbleEllipsesOutline} size="large"></IonIcon>
|
|
||||||
<IonLabel>Demo React WhatsApp Clone</IonLabel>
|
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
|
||||||
</IonItem>
|
|
||||||
</IonList>
|
|
||||||
|
|
||||||
<IonList inset={false}>
|
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_SKELETON_TEXT, 'forward')):
|
|
||||||
<IonIcon slot="start" icon={codeWorkingOutline} size="large"></IonIcon>
|
<IonIcon slot="start" icon={codeWorkingOutline} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Skeleton Text</IonLabel>
|
<IonLabel>Demo Skeleton Text</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -472,7 +257,10 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_STICKY_BOTTOM_SHEET_EXAMPLE, 'forward')):
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_STICKY_BOTTOM_SHEET_EXAMPLE, 'forward')}
|
||||||
|
>
|
||||||
<IonIcon slot="start" icon={paperPlaneOutline} size="large"></IonIcon>
|
<IonIcon slot="start" icon={paperPlaneOutline} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Sticky Bottom Sheet Example</IonLabel>
|
<IonLabel>Demo Sticky Bottom Sheet Example</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -480,15 +268,21 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_STORAGE_EXAMPLE, 'forward')):
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_STORAGE_EXAMPLE, 'forward')}>
|
||||||
<IonIcon slot="start" icon={cloudOutline} size="large"></IonIcon>
|
<IonIcon slot="start" icon={cloudOutline} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Storage Example</IonLabel>
|
<IonLabel>
|
||||||
|
Demo Storage Example{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>(need fix, message cannot display)</span>
|
||||||
|
</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_SWIPERJS_TUTORIAL, 'forward')):
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_SWIPERJS_TUTORIAL, 'forward')}
|
||||||
|
>
|
||||||
<IonIcon slot="start" icon={imagesOutline} size="large"></IonIcon>
|
<IonIcon slot="start" icon={imagesOutline} size="large"></IonIcon>
|
||||||
<IonLabel>Demo SwiperJS Tutorial</IonLabel>
|
<IonLabel>Demo SwiperJS Tutorial</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -496,19 +290,241 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_WEATHER_APP_UI, 'forward')):
|
<IonItem
|
||||||
<IonIcon slot="start" icon={sunnyOutline} size="large"></IonIcon>
|
button={true}
|
||||||
<IonLabel>Demo Weather App UI</IonLabel>
|
onClick={() => router.push(PATHS.DEMO_REACT_DRAWING_CANVAS, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={brushOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Drawing Canvas</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
*/}
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_HOOK_FORM_EXAMPLE, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={codeSlashOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Hook Form Example</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_2FA_EXAMPLE, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_ITEM_LIST, 'forward')}>
|
||||||
<IonIcon slot="start" icon={keyOutline} size="large"></IonIcon>
|
<IonIcon slot="start" icon={listOutline} size="large"></IonIcon>
|
||||||
<IonLabel>Demo 2FA Example</IonLabel>
|
<IonLabel>Demo React Item List</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_LIFECYCLES, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={refreshOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Lifecycles</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_LOGIN, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={logInOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo React Login <span style={{ fontWeight: 'bold' }}>(missing back button)</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_MARVEL_APP, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={flashOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Marvel App</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_MOVIE_APP_WITH_ALGOLIA, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={filmOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Movie App with Algolia</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_NOTES, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={documentTextOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo React Notes{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>TODO: need update IonSlide</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_FACEBOOK_CLONE, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={logoFacebook} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo Facebook Clone</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_FAST_FOOD_APP, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={restaurantOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo Fast Food App <span style={{ fontWeight: 'bold' }}>ion-slide outstanding</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_FLOATING_TABS, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={layersOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo Floating Tabs</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_INSTAGRAM_CLONE, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={imageOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo Instagram Clone</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_KANBAN_BOARD, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={gridOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo Kanban Board{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>// TODO: fix missing ionslide in new ionic</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_ORDERING_APP, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={pizzaOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo Ordering App <span style={{ fontWeight: 'bold' }}>outstanding css</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_PROFILE_EXAMPLE, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={personOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo Profile Example</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_PULLSTATE_TUTORIAL, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={codeWorkingOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo Pullstate Tutorial</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_ADD_TO_CART, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Add to Cart</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_CALCULATOR, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={calculatorOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo React Calculator <span style={{ fontWeight: 'bold' }}>css need fix</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_ACCORDION_TUTORIAL, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={list} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo Accordion Tutorial</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_BANKING_UI, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={cashOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo Banking UI{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>(in the middle, style outstanding)</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={mapOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo Capacitor Google Maps Tutorial{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>need a google map api key</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_COLOR_TUTORIAL, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={colorPaletteOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo Color Tutorial</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_ECOMMERCE_EXAMPLE, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo Ecommerce Example{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>(fetch data not available at remote site)</span>
|
||||||
|
</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
@@ -526,27 +542,35 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
*/}
|
*/}
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_POLL_APP, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_POLL_APP, 'forward')}>
|
||||||
<IonIcon slot="start" icon={statsChart} size="large"></IonIcon>
|
<IonIcon slot="start" icon={statsChart} size="large"></IonIcon>
|
||||||
<IonLabel>
|
<IonLabel>
|
||||||
Demo React Poll App <span style={{ fontWeight: 'bold' }}>(css temporary broken, ignored)</span>
|
Demo React Poll App{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>(css temporary broken, ignored)</span>
|
||||||
</IonLabel>
|
</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_SWITCH_TABS, 'forward')}>
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_SWITCH_TABS, 'forward')}
|
||||||
|
>
|
||||||
<IonIcon slot="start" icon={swapHorizontal} size="large"></IonIcon>
|
<IonIcon slot="start" icon={swapHorizontal} size="large"></IonIcon>
|
||||||
<IonLabel>
|
<IonLabel>
|
||||||
Demo React Switch Tabs <span style={{ fontWeight: 'bold' }}>(hardcoded back button)</span>
|
Demo React Switch Tabs{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>(hardcoded back button)</span>
|
||||||
</IonLabel>
|
</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_OVERLAY_HOOKS, 'forward')}>
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_OVERLAY_HOOKS, 'forward')}
|
||||||
|
>
|
||||||
<IonIcon slot="start" icon={layers} size="large"></IonIcon>
|
<IonIcon slot="start" icon={layers} size="large"></IonIcon>
|
||||||
<IonLabel>Demo React Overlay Hooks</IonLabel>
|
<IonLabel>Demo React Overlay Hooks</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -554,20 +578,28 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_PINTEREST_FLOATING_TAB_BAR, 'forward')}>
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_PINTEREST_FLOATING_TAB_BAR, 'forward')}
|
||||||
|
>
|
||||||
<IonIcon slot="start" icon={people} size="large"></IonIcon>
|
<IonIcon slot="start" icon={people} size="large"></IonIcon>
|
||||||
<IonLabel>
|
<IonLabel>
|
||||||
Demo Pinterest Floating Tab Bar <span style={{ fontWeight: 'bold' }}>(css not work well)</span>
|
Demo Pinterest Floating Tab Bar{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>(css not work well)</span>
|
||||||
</IonLabel>
|
</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_RESTAURANT_FINDER, 'forward')}>
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_RESTAURANT_FINDER, 'forward')}
|
||||||
|
>
|
||||||
<IonIcon slot="start" icon={restaurant} size="large"></IonIcon>
|
<IonIcon slot="start" icon={restaurant} size="large"></IonIcon>
|
||||||
<IonLabel>
|
<IonLabel>
|
||||||
Demo Restaurant Finder <span style={{ fontWeight: 'bold' }}>need server for map showing</span>
|
Demo Restaurant Finder{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>need server for map showing</span>
|
||||||
</IonLabel>
|
</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
@@ -585,7 +617,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
<IonItem
|
<IonItem
|
||||||
button={true}
|
button={true}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(paths.DEMO_CLUB_HOUSE, 'forward');
|
router.push(PATHS.DEMO_CLUB_HOUSE, 'forward');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
||||||
@@ -598,7 +630,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
<IonItem
|
<IonItem
|
||||||
button={true}
|
button={true}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(paths.DEMO_SCORE_BOARD, 'forward');
|
router.push(PATHS.DEMO_SCORE_BOARD, 'forward');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<IonIcon slot="start" icon={car} size="large"></IonIcon>
|
<IonIcon slot="start" icon={car} size="large"></IonIcon>
|
||||||
@@ -611,7 +643,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_QUOTE_APP, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_QUOTE_APP, 'forward')}>
|
||||||
<IonIcon slot="start" icon={car} size="large"></IonIcon>
|
<IonIcon slot="start" icon={car} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Quote App</IonLabel>
|
<IonLabel>Demo Quote App</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -619,7 +651,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_QR_SCANNER, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_QR_SCANNER, 'forward')}>
|
||||||
<IonIcon slot="start" icon={car} size="large"></IonIcon>
|
<IonIcon slot="start" icon={car} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Qr scanner</IonLabel>
|
<IonLabel>Demo Qr scanner</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -627,7 +659,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_SHOP_APP_UI, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_SHOP_APP_UI, 'forward')}>
|
||||||
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Shop App UI</IonLabel>
|
<IonLabel>Demo Shop App UI</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -635,7 +667,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_DICTIONARY_APP, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_DICTIONARY_APP, 'forward')}>
|
||||||
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Dictionary App</IonLabel>
|
<IonLabel>Demo Dictionary App</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -643,7 +675,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_RECIPE_APP, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_RECIPE_APP, 'forward')}>
|
||||||
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Recipe App</IonLabel>
|
<IonLabel>Demo Recipe App</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -651,7 +683,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_SLIDING_PROFILE, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_SLIDING_PROFILE, 'forward')}>
|
||||||
<IonIcon slot="start" icon={person} size="large"></IonIcon>
|
<IonIcon slot="start" icon={person} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Sliding Profile</IonLabel>
|
<IonLabel>Demo Sliding Profile</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -659,7 +691,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_QUIZ_APP, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_QUIZ_APP, 'forward')}>
|
||||||
<IonIcon slot="start" icon={book} size="large"></IonIcon>
|
<IonIcon slot="start" icon={book} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Quiz App</IonLabel>
|
<IonLabel>Demo Quiz App</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -667,7 +699,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_BLOG_POST_UI, 'forward')}>
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_BLOG_POST_UI, 'forward')}>
|
||||||
<IonIcon slot="start" icon={document} size="large"></IonIcon>
|
<IonIcon slot="start" icon={document} size="large"></IonIcon>
|
||||||
<IonLabel>Demo Blog Post UI</IonLabel>
|
<IonLabel>Demo Blog Post UI</IonLabel>
|
||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
@@ -675,7 +707,10 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
<IonList inset={false}>
|
<IonList inset={false}>
|
||||||
<IonItem button={true} onClick={() => router.push(paths.DEMO_REACT_TRAVEL_APP, 'forward')}>
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_TRAVEL_APP, 'forward')}
|
||||||
|
>
|
||||||
<IonIcon slot="start" icon={globeSharp} size="large"></IonIcon>
|
<IonIcon slot="start" icon={globeSharp} size="large"></IonIcon>
|
||||||
<IonLabel>
|
<IonLabel>
|
||||||
Demo React Travel App <span style={{ fontWeight: 'bold' }}>(on hold)</span>
|
Demo React Travel App <span style={{ fontWeight: 'bold' }}>(on hold)</span>
|
||||||
@@ -683,10 +718,80 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
|||||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
</IonItem>
|
</IonItem>
|
||||||
</IonList>
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_PROFILE_DASHBOARD_UI, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={personCircleOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Profile Dashboard UI</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
{/* TODO: */}
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_QR_CODE, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={qrCodeOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo React QR Code <span style={{ fontWeight: 'bold' }}>need update</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_QUOTES, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={bookOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Quotes</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_REACT_SHOP_UI, 'forward')}>
|
||||||
|
<IonIcon slot="start" icon={cartOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo React Shop UI <span style={{ fontWeight: 'bold' }}>lower priority</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_TABS_MENUS_CUSTOM, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={layersOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>Demo React Tabs Menus Custom</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
|
|
||||||
|
<IonList inset={false}>
|
||||||
|
<IonItem
|
||||||
|
button={true}
|
||||||
|
onClick={() => router.push(PATHS.DEMO_REACT_ONBOARDING_UI, 'forward')}
|
||||||
|
>
|
||||||
|
<IonIcon slot="start" icon={walkOutline} size="large"></IonIcon>
|
||||||
|
<IonLabel>
|
||||||
|
Demo React Onboarding UI{' '}
|
||||||
|
<span style={{ fontWeight: 'bold' }}>TODO: update IonSlide</span>
|
||||||
|
</IonLabel>
|
||||||
|
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||||
|
</IonItem>
|
||||||
|
</IonList>
|
||||||
</IonContent>
|
</IonContent>
|
||||||
|
|
||||||
{/* REQ0058/logout */}
|
{/* REQ0058/logout */}
|
||||||
<IonModal isOpen={showLogoutConfirmModal} initialBreakpoint={0.5} breakpoints={[0, 0.25, 0.5, 0.75]}>
|
<IonModal
|
||||||
|
isOpen={showLogoutConfirmModal}
|
||||||
|
initialBreakpoint={0.5}
|
||||||
|
breakpoints={[0, 0.25, 0.5, 0.75]}
|
||||||
|
>
|
||||||
<IonContent
|
<IonContent
|
||||||
className="ion-padding"
|
className="ion-padding"
|
||||||
style={{
|
style={{
|
||||||
@@ -752,5 +857,5 @@ export default connect<OwnProps, StateProps, DispatchProps>({
|
|||||||
setAccessToken,
|
setAccessToken,
|
||||||
setIsLoggedIn,
|
setIsLoggedIn,
|
||||||
},
|
},
|
||||||
component: React.memo(SettingsPage),
|
component: React.memo(DemoList),
|
||||||
});
|
});
|
||||||
|
@@ -51,8 +51,8 @@ import { CompletedCard } from '../components/CompletedCard';
|
|||||||
import { QuizStats } from '../components/QuizStats';
|
import { QuizStats } from '../components/QuizStats';
|
||||||
|
|
||||||
const Questions = () => {
|
const Questions = () => {
|
||||||
const mainContainerRef = useRef();
|
const mainContainerRef = useRef(null);
|
||||||
const completionContainerRef = useRef();
|
const completionContainerRef = useRef(null);
|
||||||
const swiperRef = useRef(null);
|
const swiperRef = useRef(null);
|
||||||
|
|
||||||
const router = useIonRouter();
|
const router = useIonRouter();
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -1,103 +0,0 @@
|
|||||||
#about-page {
|
|
||||||
ion-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
--background: transparent;
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-toolbar ion-back-button,
|
|
||||||
ion-toolbar ion-button,
|
|
||||||
ion-toolbar ion-menu-button {
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .about-image {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
transition: opacity 500ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .madison {
|
|
||||||
background-image: url('/assets/ScoreBoard/img/about/madison.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .austin {
|
|
||||||
background-image: url('/assets/ScoreBoard/img/about/austin.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .chicago {
|
|
||||||
background-image: url('/assets/ScoreBoard/img/about/chicago.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .seattle {
|
|
||||||
background-image: url('/assets/ScoreBoard/img/about/seattle.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info {
|
|
||||||
position: relative;
|
|
||||||
margin-top: -10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--ion-background-color, #fff);
|
|
||||||
z-index: 2; // display rounded border above header image
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-list {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info p {
|
|
||||||
line-height: 130%;
|
|
||||||
|
|
||||||
color: var(--ion-color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-icon {
|
|
||||||
margin-inline-end: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios .about-info {
|
|
||||||
--ion-padding: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios .about-info h3 {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#date-input-popover {
|
|
||||||
--offset-y: -var(--ion-safe-area-bottom);
|
|
||||||
|
|
||||||
--max-width: 90%;
|
|
||||||
--width: 336px;
|
|
||||||
}
|
|
||||||
|
@@ -1,79 +0,0 @@
|
|||||||
.demo-react-add-to-cart {
|
|
||||||
/* Ionic Variables and Theming. For more info, please see:
|
|
||||||
http://ionicframework.com/docs/theming/ */
|
|
||||||
|
|
||||||
/** Ionic CSS Variables **/
|
|
||||||
:root {
|
|
||||||
/** primary **/
|
|
||||||
--ion-color-primary: #3880ff;
|
|
||||||
--ion-color-primary-rgb: 56, 128, 255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-primary-shade: #3171e0;
|
|
||||||
--ion-color-primary-tint: #4c8dff;
|
|
||||||
|
|
||||||
/** secondary **/
|
|
||||||
--ion-color-secondary: #3dc2ff;
|
|
||||||
--ion-color-secondary-rgb: 61, 194, 255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-secondary-shade: #36abe0;
|
|
||||||
--ion-color-secondary-tint: #50c8ff;
|
|
||||||
|
|
||||||
/** tertiary **/
|
|
||||||
--ion-color-tertiary: #5260ff;
|
|
||||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-tertiary-shade: #4854e0;
|
|
||||||
--ion-color-tertiary-tint: #6370ff;
|
|
||||||
|
|
||||||
/** success **/
|
|
||||||
--ion-color-success: #2dd36f;
|
|
||||||
--ion-color-success-rgb: 45, 211, 111;
|
|
||||||
--ion-color-success-contrast: #ffffff;
|
|
||||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-success-shade: #28ba62;
|
|
||||||
--ion-color-success-tint: #42d77d;
|
|
||||||
|
|
||||||
/** warning **/
|
|
||||||
--ion-color-warning: #ffc409;
|
|
||||||
--ion-color-warning-rgb: 255, 196, 9;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-warning-shade: #e0ac08;
|
|
||||||
--ion-color-warning-tint: #ffca22;
|
|
||||||
|
|
||||||
/** danger **/
|
|
||||||
--ion-color-danger: #eb445a;
|
|
||||||
--ion-color-danger-rgb: 235, 68, 90;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-danger-shade: #cf3c4f;
|
|
||||||
--ion-color-danger-tint: #ed576b;
|
|
||||||
|
|
||||||
/** dark **/
|
|
||||||
--ion-color-dark: #222428;
|
|
||||||
--ion-color-dark-rgb: 34, 36, 40;
|
|
||||||
--ion-color-dark-contrast: #ffffff;
|
|
||||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-dark-shade: #1e2023;
|
|
||||||
--ion-color-dark-tint: #383a3e;
|
|
||||||
|
|
||||||
/** medium **/
|
|
||||||
--ion-color-medium: #92949c;
|
|
||||||
--ion-color-medium-rgb: 146, 148, 156;
|
|
||||||
--ion-color-medium-contrast: #ffffff;
|
|
||||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-medium-shade: #808289;
|
|
||||||
--ion-color-medium-tint: #9d9fa6;
|
|
||||||
|
|
||||||
/** light **/
|
|
||||||
--ion-color-light: #f4f5f8;
|
|
||||||
--ion-color-light-rgb: 244, 245, 248;
|
|
||||||
--ion-color-light-contrast: #000000;
|
|
||||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-light-shade: #d7d8da;
|
|
||||||
--ion-color-light-tint: #f5f6f9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,83 +0,0 @@
|
|||||||
.demo-react-calculator {
|
|
||||||
/* Ionic Variables and Theming. For more info, please see:
|
|
||||||
http://ionicframework.com/docs/theming/ */
|
|
||||||
|
|
||||||
/** Ionic CSS Variables **/
|
|
||||||
:root {
|
|
||||||
/** primary **/
|
|
||||||
--ion-color-primary: #3880ff;
|
|
||||||
--ion-color-primary-rgb: 56, 128, 255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-primary-shade: #3171e0;
|
|
||||||
--ion-color-primary-tint: #4c8dff;
|
|
||||||
|
|
||||||
/** secondary **/
|
|
||||||
--ion-color-secondary: #3dc2ff;
|
|
||||||
--ion-color-secondary-rgb: 61, 194, 255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-secondary-shade: #36abe0;
|
|
||||||
--ion-color-secondary-tint: #50c8ff;
|
|
||||||
|
|
||||||
/** tertiary **/
|
|
||||||
--ion-color-tertiary: #5260ff;
|
|
||||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-tertiary-shade: #4854e0;
|
|
||||||
--ion-color-tertiary-tint: #6370ff;
|
|
||||||
|
|
||||||
/** success **/
|
|
||||||
--ion-color-success: #2dd36f;
|
|
||||||
--ion-color-success-rgb: 45, 211, 111;
|
|
||||||
--ion-color-success-contrast: #ffffff;
|
|
||||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-success-shade: #28ba62;
|
|
||||||
--ion-color-success-tint: #42d77d;
|
|
||||||
|
|
||||||
/** warning **/
|
|
||||||
--ion-color-warning: #ffc409;
|
|
||||||
--ion-color-warning-rgb: 255, 196, 9;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-warning-shade: #e0ac08;
|
|
||||||
--ion-color-warning-tint: #ffca22;
|
|
||||||
|
|
||||||
/** danger **/
|
|
||||||
--ion-color-danger: #eb445a;
|
|
||||||
--ion-color-danger-rgb: 235, 68, 90;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-danger-shade: #cf3c4f;
|
|
||||||
--ion-color-danger-tint: #ed576b;
|
|
||||||
|
|
||||||
/** dark **/
|
|
||||||
--ion-color-dark: #222428;
|
|
||||||
--ion-color-dark-rgb: 34, 36, 40;
|
|
||||||
--ion-color-dark-contrast: #ffffff;
|
|
||||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-dark-shade: #1e2023;
|
|
||||||
--ion-color-dark-tint: #383a3e;
|
|
||||||
|
|
||||||
/** medium **/
|
|
||||||
--ion-color-medium: #92949c;
|
|
||||||
--ion-color-medium-rgb: 146, 148, 156;
|
|
||||||
--ion-color-medium-contrast: #ffffff;
|
|
||||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-medium-shade: #808289;
|
|
||||||
--ion-color-medium-tint: #9d9fa6;
|
|
||||||
|
|
||||||
/** light **/
|
|
||||||
--ion-color-light: #f4f5f8;
|
|
||||||
--ion-color-light-rgb: 244, 245, 248;
|
|
||||||
--ion-color-light-contrast: #000000;
|
|
||||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-light-shade: #d7d8da;
|
|
||||||
--ion-color-light-tint: #f5f6f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--blue-color: rgb(99, 158, 226);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,79 +0,0 @@
|
|||||||
.demo-react-drawing-canvas {
|
|
||||||
/* Ionic Variables and Theming. For more info, please see:
|
|
||||||
http://ionicframework.com/docs/theming/ */
|
|
||||||
|
|
||||||
/** Ionic CSS Variables **/
|
|
||||||
:root {
|
|
||||||
/** primary **/
|
|
||||||
--ion-color-primary: #3880ff;
|
|
||||||
--ion-color-primary-rgb: 56, 128, 255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-primary-shade: #3171e0;
|
|
||||||
--ion-color-primary-tint: #4c8dff;
|
|
||||||
|
|
||||||
/** secondary **/
|
|
||||||
--ion-color-secondary: #3dc2ff;
|
|
||||||
--ion-color-secondary-rgb: 61, 194, 255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-secondary-shade: #36abe0;
|
|
||||||
--ion-color-secondary-tint: #50c8ff;
|
|
||||||
|
|
||||||
/** tertiary **/
|
|
||||||
--ion-color-tertiary: #5260ff;
|
|
||||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-tertiary-shade: #4854e0;
|
|
||||||
--ion-color-tertiary-tint: #6370ff;
|
|
||||||
|
|
||||||
/** success **/
|
|
||||||
--ion-color-success: #2dd36f;
|
|
||||||
--ion-color-success-rgb: 45, 211, 111;
|
|
||||||
--ion-color-success-contrast: #ffffff;
|
|
||||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-success-shade: #28ba62;
|
|
||||||
--ion-color-success-tint: #42d77d;
|
|
||||||
|
|
||||||
/** warning **/
|
|
||||||
--ion-color-warning: #ffc409;
|
|
||||||
--ion-color-warning-rgb: 255, 196, 9;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-warning-shade: #e0ac08;
|
|
||||||
--ion-color-warning-tint: #ffca22;
|
|
||||||
|
|
||||||
/** danger **/
|
|
||||||
--ion-color-danger: #eb445a;
|
|
||||||
--ion-color-danger-rgb: 235, 68, 90;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-danger-shade: #cf3c4f;
|
|
||||||
--ion-color-danger-tint: #ed576b;
|
|
||||||
|
|
||||||
/** dark **/
|
|
||||||
--ion-color-dark: #222428;
|
|
||||||
--ion-color-dark-rgb: 34, 36, 40;
|
|
||||||
--ion-color-dark-contrast: #ffffff;
|
|
||||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-dark-shade: #1e2023;
|
|
||||||
--ion-color-dark-tint: #383a3e;
|
|
||||||
|
|
||||||
/** medium **/
|
|
||||||
--ion-color-medium: #92949c;
|
|
||||||
--ion-color-medium-rgb: 146, 148, 156;
|
|
||||||
--ion-color-medium-contrast: #ffffff;
|
|
||||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-medium-shade: #808289;
|
|
||||||
--ion-color-medium-tint: #9d9fa6;
|
|
||||||
|
|
||||||
/** light **/
|
|
||||||
--ion-color-light: #f4f5f8;
|
|
||||||
--ion-color-light-rgb: 244, 245, 248;
|
|
||||||
--ion-color-light-contrast: #000000;
|
|
||||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-light-shade: #d7d8da;
|
|
||||||
--ion-color-light-tint: #f5f6f9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,103 +0,0 @@
|
|||||||
#about-page {
|
|
||||||
ion-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
--background: transparent;
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-toolbar ion-back-button,
|
|
||||||
ion-toolbar ion-button,
|
|
||||||
ion-toolbar ion-menu-button {
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .about-image {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
transition: opacity 500ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .madison {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .austin {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .chicago {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .seattle {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info {
|
|
||||||
position: relative;
|
|
||||||
margin-top: -10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--ion-background-color, #fff);
|
|
||||||
z-index: 2; // display rounded border above header image
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-list {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info p {
|
|
||||||
line-height: 130%;
|
|
||||||
|
|
||||||
color: var(--ion-color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-icon {
|
|
||||||
margin-inline-end: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios .about-info {
|
|
||||||
--ion-padding: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios .about-info h3 {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#date-input-popover {
|
|
||||||
--offset-y: -var(--ion-safe-area-bottom);
|
|
||||||
|
|
||||||
--max-width: 90%;
|
|
||||||
--width: 336px;
|
|
||||||
}
|
|
||||||
|
@@ -1,79 +0,0 @@
|
|||||||
.demo-react-hook-form-example {
|
|
||||||
/* Ionic Variables and Theming. For more info, please see:
|
|
||||||
http://ionicframework.com/docs/theming/ */
|
|
||||||
|
|
||||||
/** Ionic CSS Variables **/
|
|
||||||
:root {
|
|
||||||
/** primary **/
|
|
||||||
--ion-color-primary: #3880ff;
|
|
||||||
--ion-color-primary-rgb: 56, 128, 255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-primary-shade: #3171e0;
|
|
||||||
--ion-color-primary-tint: #4c8dff;
|
|
||||||
|
|
||||||
/** secondary **/
|
|
||||||
--ion-color-secondary: #3dc2ff;
|
|
||||||
--ion-color-secondary-rgb: 61, 194, 255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-secondary-shade: #36abe0;
|
|
||||||
--ion-color-secondary-tint: #50c8ff;
|
|
||||||
|
|
||||||
/** tertiary **/
|
|
||||||
--ion-color-tertiary: #5260ff;
|
|
||||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-tertiary-shade: #4854e0;
|
|
||||||
--ion-color-tertiary-tint: #6370ff;
|
|
||||||
|
|
||||||
/** success **/
|
|
||||||
--ion-color-success: #2dd36f;
|
|
||||||
--ion-color-success-rgb: 45, 211, 111;
|
|
||||||
--ion-color-success-contrast: #ffffff;
|
|
||||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-success-shade: #28ba62;
|
|
||||||
--ion-color-success-tint: #42d77d;
|
|
||||||
|
|
||||||
/** warning **/
|
|
||||||
--ion-color-warning: #ffc409;
|
|
||||||
--ion-color-warning-rgb: 255, 196, 9;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-warning-shade: #e0ac08;
|
|
||||||
--ion-color-warning-tint: #ffca22;
|
|
||||||
|
|
||||||
/** danger **/
|
|
||||||
--ion-color-danger: #eb445a;
|
|
||||||
--ion-color-danger-rgb: 235, 68, 90;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-danger-shade: #cf3c4f;
|
|
||||||
--ion-color-danger-tint: #ed576b;
|
|
||||||
|
|
||||||
/** dark **/
|
|
||||||
--ion-color-dark: #222428;
|
|
||||||
--ion-color-dark-rgb: 34, 36, 40;
|
|
||||||
--ion-color-dark-contrast: #ffffff;
|
|
||||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-dark-shade: #1e2023;
|
|
||||||
--ion-color-dark-tint: #383a3e;
|
|
||||||
|
|
||||||
/** medium **/
|
|
||||||
--ion-color-medium: #92949c;
|
|
||||||
--ion-color-medium-rgb: 146, 148, 156;
|
|
||||||
--ion-color-medium-contrast: #ffffff;
|
|
||||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-medium-shade: #808289;
|
|
||||||
--ion-color-medium-tint: #9d9fa6;
|
|
||||||
|
|
||||||
/** light **/
|
|
||||||
--ion-color-light: #f4f5f8;
|
|
||||||
--ion-color-light-rgb: 244, 245, 248;
|
|
||||||
--ion-color-light-contrast: #000000;
|
|
||||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-light-shade: #d7d8da;
|
|
||||||
--ion-color-light-tint: #f5f6f9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container strong {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container p {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
import './ExploreContainer.css';
|
||||||
|
|
||||||
|
const ExploreContainer = () => {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<strong>Ready to create an app?</strong>
|
||||||
|
<p>Start with Ionic <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExploreContainer;
|
@@ -1,28 +1,26 @@
|
|||||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
import { IonRouterOutlet, IonTabs } from '@ionic/react';
|
||||||
|
|
||||||
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
|
||||||
import { Route, Redirect } from 'react-router';
|
import { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
import './theme/variables.scss';
|
||||||
import Tab2 from './AppPages/Tab2';
|
import Home from './pages/Home';
|
||||||
|
import Movies from './pages/Movies';
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
function DemoReactItemList() {
|
function DemoReactItemList() {
|
||||||
return (
|
return (
|
||||||
<IonTabs>
|
<IonTabs>
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path="/demo-react-item-list/tab1">
|
<Route exact path="/demo-react-item-list/home">
|
||||||
<Tab1 />
|
<Home />
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path="/demo-react-item-list/tab2">
|
<Route exact path="/demo-react-item-list/movies">
|
||||||
<Tab2 />
|
<Movies />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Redirect exact path="/demo-react-item-list" to="/demo-react-item-list/tab1" />
|
<Redirect exact path="/demo-react-item-list" to="/demo-react-item-list/home" />
|
||||||
</IonRouterOutlet>
|
</IonRouterOutlet>
|
||||||
|
|
||||||
{/* */}
|
{/*
|
||||||
<IonTabBar slot="bottom">
|
<IonTabBar slot="bottom">
|
||||||
<IonTabButton tab="tab1" href="/demo-react-item-list/tab1">
|
<IonTabButton tab="tab1" href="/demo-react-item-list/tab1">
|
||||||
<IonIcon icon={cloudOutline} />
|
<IonIcon icon={cloudOutline} />
|
||||||
@@ -33,6 +31,7 @@ function DemoReactItemList() {
|
|||||||
<IonLabel>Search</IonLabel>
|
<IonLabel>Search</IonLabel>
|
||||||
</IonTabButton>
|
</IonTabButton>
|
||||||
</IonTabBar>
|
</IonTabBar>
|
||||||
|
*/}
|
||||||
</IonTabs>
|
</IonTabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
9
03_source/mobile/src/pages/DemoReactItemList/module.d.ts
vendored
Normal file
9
03_source/mobile/src/pages/DemoReactItemList/module.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
declare module '*.module.css' {
|
||||||
|
const classes: { readonly [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.module.scss' {
|
||||||
|
const classes: { readonly [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
@@ -0,0 +1,127 @@
|
|||||||
|
.page {
|
||||||
|
ion-header {
|
||||||
|
background-color: #5a55ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-toolbar {
|
||||||
|
--border-style: none;
|
||||||
|
--background: #5a55ca;
|
||||||
|
--color: white;
|
||||||
|
--min-height: 8rem;
|
||||||
|
|
||||||
|
// --stripe: #645fd1;
|
||||||
|
--stripe: #5a55ca;
|
||||||
|
--bg: #645fd1;
|
||||||
|
--background: transparent;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, var(--bg) 25%, transparent 25%) -50px 0,
|
||||||
|
linear-gradient(225deg, var(--bg) 25%, transparent 25%) -50px 0,
|
||||||
|
linear-gradient(315deg, var(--bg) 25%, transparent 25%),
|
||||||
|
linear-gradient(45deg, var(--bg) 25%, transparent 25%);
|
||||||
|
background-size: 100px 100px;
|
||||||
|
background-color: var(--stripe);
|
||||||
|
|
||||||
|
ion-button {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-content {
|
||||||
|
--background: #5a55ca;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-card-subtitle {
|
||||||
|
padding-left: 1.2rem;
|
||||||
|
margin-top: 1.9rem;
|
||||||
|
padding-bottom: 0.3rem;
|
||||||
|
--color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results {
|
||||||
|
--color: #5a55ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-searchbar {
|
||||||
|
--border-radius: 10px;
|
||||||
|
--background: white;
|
||||||
|
--color: black;
|
||||||
|
min-height: 5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
padding-top: 0;
|
||||||
|
margin-top: -1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-list {
|
||||||
|
background-color: #e7edfb;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-footer {
|
||||||
|
background-color: #e7edfb;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
ion-button {
|
||||||
|
--background: #5a55ca;
|
||||||
|
--background-focused: #6f6bbb;
|
||||||
|
--background-activated: #6f6bbb;
|
||||||
|
--padding-top: 1rem;
|
||||||
|
--padding-bottom: 1rem;
|
||||||
|
--padding-start: 0.75rem;
|
||||||
|
--padding-end: 0.75rem;
|
||||||
|
height: 3.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainContent {
|
||||||
|
background-color: #e7edfb;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: scroll !important;
|
||||||
|
border-top-left-radius: 30px;
|
||||||
|
border-top-right-radius: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.employeeItem {
|
||||||
|
--border-radius: 10px;
|
||||||
|
--padding-start: 1rem;
|
||||||
|
--padding-end: 1rem;
|
||||||
|
--padding-top: 1rem;
|
||||||
|
--padding-bottom: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
min-height: 5rem;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 3rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #e7edfb;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-label {
|
||||||
|
padding-left: 1.2rem;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: -0.02rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
letter-spacing: -0.03rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-button {
|
||||||
|
--background: #5a55ca;
|
||||||
|
--background-focused: #6f6bbb;
|
||||||
|
--background-activated: #6f6bbb;
|
||||||
|
--padding-top: 1rem;
|
||||||
|
--padding-bottom: 1rem;
|
||||||
|
--padding-start: 0.75rem;
|
||||||
|
--padding-end: 0.75rem;
|
||||||
|
margin-top: -0.2rem;
|
||||||
|
}
|
||||||
|
}
|
152
03_source/mobile/src/pages/DemoReactItemList/pages/Home.tsx
Normal file
152
03_source/mobile/src/pages/DemoReactItemList/pages/Home.tsx
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import {
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCardSubtitle,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonList,
|
||||||
|
IonPage,
|
||||||
|
IonSearchbar,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { chevronBack, chevronForward, trashOutline } from 'ionicons/icons';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import styles from './Home.module.scss';
|
||||||
|
|
||||||
|
const Home = () => {
|
||||||
|
const [employees, setEmployees] = useState([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Alan Montgomery',
|
||||||
|
title: 'Mobile Team Lead',
|
||||||
|
avatar: 'https://pbs.twimg.com/profile_images/1383061489469292548/5dhsPd4j_400x400.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'Max Lynch',
|
||||||
|
title: 'CEO | Co Founder',
|
||||||
|
avatar: 'https://pbs.twimg.com/profile_images/1318970727173885953/bln98FNj_400x400.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Mike Hartington',
|
||||||
|
title: 'Senior Dev Rel',
|
||||||
|
avatar: 'https://pbs.twimg.com/profile_images/1084993841898446849/DJ8XtR6L_400x400.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: 'Matt Netkow',
|
||||||
|
title: 'Head of Product Marketing',
|
||||||
|
avatar: 'https://pbs.twimg.com/profile_images/1323383930150621187/GKc0nVzi_400x400.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: 'Ben Sperry',
|
||||||
|
title: 'CDO | Co Founder',
|
||||||
|
avatar: 'https://pbs.twimg.com/profile_images/1328390491126308864/jHHgl5Dm_400x400.jpg',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: 'Liam DeBeasi',
|
||||||
|
title: 'Software Engineer',
|
||||||
|
avatar: 'https://pbs.twimg.com/profile_images/1105953692669366273/ZNK4lRAJ_400x400.jpg',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [results, setResults] = useState(employees);
|
||||||
|
|
||||||
|
const remove = (id) => {
|
||||||
|
document.getElementById(`employeeItem_${id}`).classList.add('animate__slideOutRight');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const tempEmployees = [...employees];
|
||||||
|
const newEmployees = tempEmployees.filter((e) => parseInt(e.id) !== parseInt(id));
|
||||||
|
setResults(newEmployees);
|
||||||
|
setEmployees(newEmployees);
|
||||||
|
}, 700);
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = (e) => {
|
||||||
|
const searchTerm = e.currentTarget.value;
|
||||||
|
|
||||||
|
if (searchTerm !== '') {
|
||||||
|
const searchTermLower = searchTerm.toLowerCase();
|
||||||
|
|
||||||
|
const newResults = employees.filter((e) => e.name.toLowerCase().includes(searchTermLower));
|
||||||
|
setResults(newResults);
|
||||||
|
} else {
|
||||||
|
setResults(employees);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage className={styles.page}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton color="light">
|
||||||
|
<IonIcon icon={chevronBack} />
|
||||||
|
Employee List
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonButton routerLink="/demo-react-item-list/movies" color="light">
|
||||||
|
Movies
|
||||||
|
<IonIcon icon={chevronForward} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<div className={styles.mainContent}>
|
||||||
|
<IonCardSubtitle className={styles.results}>
|
||||||
|
{results.length} {results.legnth === 1 ? 'employee' : 'employees'} found
|
||||||
|
</IonCardSubtitle>
|
||||||
|
<IonSearchbar
|
||||||
|
onKeyUp={(e) => search(e)}
|
||||||
|
onKeyPress={(e) => search(e)}
|
||||||
|
placeholder="Search..."
|
||||||
|
icon={search}
|
||||||
|
slot="end"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IonList>
|
||||||
|
{results.map((employee, index) => {
|
||||||
|
return (
|
||||||
|
<IonItem
|
||||||
|
id={`employeeItem_${employee.id}`}
|
||||||
|
className={` ${styles.employeeItem} animate__animated animate__fadeIn`}
|
||||||
|
key={employee.id}
|
||||||
|
lines="none"
|
||||||
|
>
|
||||||
|
<img src={employee.avatar} alt="employee avatar" />
|
||||||
|
|
||||||
|
<IonLabel>
|
||||||
|
<h2>{employee.name}</h2>
|
||||||
|
<p>{employee.title}</p>
|
||||||
|
</IonLabel>
|
||||||
|
|
||||||
|
<IonButton onClick={() => remove(employee.id)}>
|
||||||
|
<IonIcon icon={trashOutline} />
|
||||||
|
</IonButton>
|
||||||
|
</IonItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</IonList>
|
||||||
|
</div>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonButton expand="block">Add new employee</IonButton>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
154
03_source/mobile/src/pages/DemoReactItemList/pages/Movies.tsx
Normal file
154
03_source/mobile/src/pages/DemoReactItemList/pages/Movies.tsx
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import { IonButton, IonButtons, IonCardSubtitle, IonContent, IonFooter, IonHeader, IonIcon, IonInfiniteScroll, IonInfiniteScrollContent, IonItem, IonLabel, IonList, IonPage, IonRow, IonSearchbar, IonToolbar } from '@ionic/react';
|
||||||
|
import { chevronBack, trashOutline } from 'ionicons/icons';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import styles from './Home.module.scss';
|
||||||
|
|
||||||
|
const Movies = () => {
|
||||||
|
|
||||||
|
const [ movies, setMovies ] = useState([]);
|
||||||
|
const [ currentPage, setCurrentPage ] = useState(1);
|
||||||
|
const [ searchTerm, setSearchTerm ] = useState("");
|
||||||
|
const [ totalResults, setTotalResults ] = useState(0);
|
||||||
|
|
||||||
|
const search = (e) => {
|
||||||
|
|
||||||
|
const searchTermVal = e.currentTarget.value;
|
||||||
|
|
||||||
|
if (searchTermVal !== "") {
|
||||||
|
|
||||||
|
const searchTermLower = searchTermVal.toLowerCase();
|
||||||
|
searchData(searchTermLower);
|
||||||
|
setSearchTerm(searchTermLower);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
getData(true, 1);
|
||||||
|
setSearchTerm("");
|
||||||
|
setCurrentPage(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchData = async (searchTermVal, page = 1) => {
|
||||||
|
|
||||||
|
const imageBaseURL = "https://image.tmdb.org/t/p/w200";
|
||||||
|
const response = await fetch(`https://api.themoviedb.org/3/search/movie?api_key=24600637ab41d89f6dd63b4c52e8b14e&query=${ searchTermVal }&page=${ page }`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
data.results.forEach(movie => {
|
||||||
|
|
||||||
|
var imageURL = "";
|
||||||
|
|
||||||
|
if (movie.poster_path !== null) {
|
||||||
|
|
||||||
|
imageURL = `${ imageBaseURL }${ movie.poster_path }`;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
imageURL = "https://critics.io/img/movies/poster-placeholder.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
movie.image = imageURL;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("in more");
|
||||||
|
console.log(data.results);
|
||||||
|
|
||||||
|
setTotalResults(data.total_results);
|
||||||
|
page === 1 ? setMovies(data.results) : setMovies([ ...movies, ...data.results ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getData = async (initialFetch = true, page = 1) => {
|
||||||
|
|
||||||
|
if (initialFetch) {
|
||||||
|
|
||||||
|
console.log("initial fetch of movies");
|
||||||
|
}
|
||||||
|
const imageBaseURL = "https://image.tmdb.org/t/p/w200";
|
||||||
|
const response = await fetch(`https://api.themoviedb.org/3/movie/popular?api_key=24600637ab41d89f6dd63b4c52e8b14e&page=${ page }`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
data.results.forEach(movie => {
|
||||||
|
|
||||||
|
var imageURL = "";
|
||||||
|
|
||||||
|
if (movie.poster_path !== null) {
|
||||||
|
|
||||||
|
imageURL = `${ imageBaseURL }${ movie.poster_path }`;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
imageURL = "https://critics.io/img/movies/poster-placeholder.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
movie.image = imageURL;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
setTotalResults(data.total_results);
|
||||||
|
initialFetch ? setMovies(data.results) : setMovies([ ...movies, ...data.results ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
getData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchMore = async e => {
|
||||||
|
|
||||||
|
console.log("in more");
|
||||||
|
const newPage = currentPage + 1;
|
||||||
|
await setCurrentPage(newPage);
|
||||||
|
|
||||||
|
searchTerm === "" ? getData(false, newPage) : searchData(searchTerm, newPage);
|
||||||
|
e.target.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage className={ styles.page }>
|
||||||
|
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton routerLink="/" routerDirection="back" color="light">
|
||||||
|
<IonIcon icon={ chevronBack } />
|
||||||
|
Movie List
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
|
||||||
|
<div className={ styles.searchContainer }>
|
||||||
|
<IonCardSubtitle>{ totalResults } { (totalResults === 1) ? "movie" : "movies" } found</IonCardSubtitle>
|
||||||
|
<IonSearchbar onKeyUp={ e => search(e) } onKeyPress={ e => search(e) } placeholder="Search..." icon={ search } slot="end" />
|
||||||
|
</div>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
|
||||||
|
<IonContent fullscreen className={ styles.content }>
|
||||||
|
<IonList>
|
||||||
|
{ movies.map((movie, index) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<IonItem id={ `employeeItem_${ movie.id }` } className={ ` ${ styles.employeeItem } animate__animated animate__fadeIn` } key={ movie.id } lines="none">
|
||||||
|
<img src={ movie.image } alt="employee avatar" />
|
||||||
|
|
||||||
|
<IonLabel>
|
||||||
|
<h2>{ movie.title }</h2>
|
||||||
|
<p>{ movie.overview }</p>
|
||||||
|
</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<IonInfiniteScroll threshold="200px" onIonInfinite={ fetchMore }>
|
||||||
|
<IonInfiniteScrollContent loadingSpinner="bubbles" loadingText="Getting more movies...">
|
||||||
|
</IonInfiniteScrollContent>
|
||||||
|
</IonInfiniteScroll>
|
||||||
|
</IonList>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonButton expand="block">Add a Movie</IonButton>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Movies;
|
1
03_source/mobile/src/pages/DemoReactItemList/react-app-env.d.ts
vendored
Normal file
1
03_source/mobile/src/pages/DemoReactItemList/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="react-scripts" />
|
@@ -1,103 +0,0 @@
|
|||||||
#about-page {
|
|
||||||
ion-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
--background: transparent;
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-toolbar ion-back-button,
|
|
||||||
ion-toolbar ion-button,
|
|
||||||
ion-toolbar ion-menu-button {
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .about-image {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
transition: opacity 500ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .madison {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .austin {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .chicago {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .seattle {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info {
|
|
||||||
position: relative;
|
|
||||||
margin-top: -10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--ion-background-color, #fff);
|
|
||||||
z-index: 2; // display rounded border above header image
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-list {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info p {
|
|
||||||
line-height: 130%;
|
|
||||||
|
|
||||||
color: var(--ion-color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-icon {
|
|
||||||
margin-inline-end: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios .about-info {
|
|
||||||
--ion-padding: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios .about-info h3 {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#date-input-popover {
|
|
||||||
--offset-y: -var(--ion-safe-area-bottom);
|
|
||||||
|
|
||||||
--max-width: 90%;
|
|
||||||
--width: 336px;
|
|
||||||
}
|
|
@@ -1,98 +0,0 @@
|
|||||||
.demo-react-item-list {
|
|
||||||
/* Ionic Variables and Theming. For more info, please see:
|
|
||||||
http://ionicframework.com/docs/theming/ */
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-feature-settings: 'calt' on;
|
|
||||||
font-feature-settings: 'calt' on;
|
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Ionic CSS Variables **/
|
|
||||||
:root {
|
|
||||||
/** primary **/
|
|
||||||
--ion-color-primary: #3880ff;
|
|
||||||
--ion-color-primary-rgb: 56, 128, 255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-primary-shade: #3171e0;
|
|
||||||
--ion-color-primary-tint: #4c8dff;
|
|
||||||
|
|
||||||
/** secondary **/
|
|
||||||
--ion-color-secondary: #3dc2ff;
|
|
||||||
--ion-color-secondary-rgb: 61, 194, 255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-secondary-shade: #36abe0;
|
|
||||||
--ion-color-secondary-tint: #50c8ff;
|
|
||||||
|
|
||||||
/** tertiary **/
|
|
||||||
--ion-color-tertiary: #5260ff;
|
|
||||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-tertiary-shade: #4854e0;
|
|
||||||
--ion-color-tertiary-tint: #6370ff;
|
|
||||||
|
|
||||||
/** success **/
|
|
||||||
--ion-color-success: #2dd36f;
|
|
||||||
--ion-color-success-rgb: 45, 211, 111;
|
|
||||||
--ion-color-success-contrast: #ffffff;
|
|
||||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-success-shade: #28ba62;
|
|
||||||
--ion-color-success-tint: #42d77d;
|
|
||||||
|
|
||||||
/** warning **/
|
|
||||||
--ion-color-warning: #ffc409;
|
|
||||||
--ion-color-warning-rgb: 255, 196, 9;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-warning-shade: #e0ac08;
|
|
||||||
--ion-color-warning-tint: #ffca22;
|
|
||||||
|
|
||||||
/** danger **/
|
|
||||||
--ion-color-danger: #eb445a;
|
|
||||||
--ion-color-danger-rgb: 235, 68, 90;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-danger-shade: #cf3c4f;
|
|
||||||
--ion-color-danger-tint: #ed576b;
|
|
||||||
|
|
||||||
/** dark **/
|
|
||||||
--ion-color-dark: #222428;
|
|
||||||
--ion-color-dark-rgb: 34, 36, 40;
|
|
||||||
--ion-color-dark-contrast: #ffffff;
|
|
||||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-dark-shade: #1e2023;
|
|
||||||
--ion-color-dark-tint: #383a3e;
|
|
||||||
|
|
||||||
/** medium **/
|
|
||||||
--ion-color-medium: #92949c;
|
|
||||||
--ion-color-medium-rgb: 146, 148, 156;
|
|
||||||
--ion-color-medium-contrast: #ffffff;
|
|
||||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-medium-shade: #808289;
|
|
||||||
--ion-color-medium-tint: #9d9fa6;
|
|
||||||
|
|
||||||
/** light **/
|
|
||||||
--ion-color-light: #f4f5f8;
|
|
||||||
--ion-color-light-rgb: 244, 245, 248;
|
|
||||||
--ion-color-light-contrast: #000000;
|
|
||||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-light-shade: #d7d8da;
|
|
||||||
--ion-color-light-tint: #f5f6f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.searchbar-input {
|
|
||||||
padding-top: 1.5rem !important;
|
|
||||||
padding-bottom: 1.5rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-searchbar ion-icon {
|
|
||||||
margin-top: 0.4rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,12 +1,7 @@
|
|||||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
import { IonRouterOutlet, IonTabs } from '@ionic/react';
|
||||||
|
|
||||||
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
|
||||||
import { Route, Redirect } from 'react-router';
|
import { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
|
||||||
import Tab2 from './AppPages/Tab2';
|
|
||||||
|
|
||||||
import './style.scss';
|
|
||||||
import Page from './pages/Page';
|
import Page from './pages/Page';
|
||||||
import Page2 from './pages/Page2';
|
import Page2 from './pages/Page2';
|
||||||
|
|
||||||
|
@@ -26,7 +26,7 @@ const Page: React.FC = () => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<IonPage>
|
<IonPage>
|
||||||
<IonHeader>
|
<IonHeader className="ion-no-border">
|
||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonButtons slot="start">
|
<IonButtons slot="start">
|
||||||
<IonMenuButton />
|
<IonMenuButton />
|
||||||
|
@@ -1,103 +0,0 @@
|
|||||||
#about-page {
|
|
||||||
ion-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
--background: transparent;
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-toolbar ion-back-button,
|
|
||||||
ion-toolbar ion-button,
|
|
||||||
ion-toolbar ion-menu-button {
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .about-image {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
transition: opacity 500ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .madison {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .austin {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .chicago {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .seattle {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info {
|
|
||||||
position: relative;
|
|
||||||
margin-top: -10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--ion-background-color, #fff);
|
|
||||||
z-index: 2; // display rounded border above header image
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-list {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info p {
|
|
||||||
line-height: 130%;
|
|
||||||
|
|
||||||
color: var(--ion-color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-icon {
|
|
||||||
margin-inline-end: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios .about-info {
|
|
||||||
--ion-padding: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios .about-info h3 {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#date-input-popover {
|
|
||||||
--offset-y: -var(--ion-safe-area-bottom);
|
|
||||||
|
|
||||||
--max-width: 90%;
|
|
||||||
--width: 336px;
|
|
||||||
}
|
|
||||||
|
@@ -1,236 +0,0 @@
|
|||||||
/* Ionic Variables and Theming. For more info, please see:
|
|
||||||
http://ionicframework.com/docs/theming/ */
|
|
||||||
|
|
||||||
/** Ionic CSS Variables **/
|
|
||||||
:root {
|
|
||||||
/** primary **/
|
|
||||||
--ion-color-primary: #3880ff;
|
|
||||||
--ion-color-primary-rgb: 56, 128, 255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-primary-shade: #3171e0;
|
|
||||||
--ion-color-primary-tint: #4c8dff;
|
|
||||||
|
|
||||||
/** secondary **/
|
|
||||||
--ion-color-secondary: #3dc2ff;
|
|
||||||
--ion-color-secondary-rgb: 61, 194, 255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-secondary-shade: #36abe0;
|
|
||||||
--ion-color-secondary-tint: #50c8ff;
|
|
||||||
|
|
||||||
/** tertiary **/
|
|
||||||
--ion-color-tertiary: #5260ff;
|
|
||||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-tertiary-shade: #4854e0;
|
|
||||||
--ion-color-tertiary-tint: #6370ff;
|
|
||||||
|
|
||||||
/** success **/
|
|
||||||
--ion-color-success: #2dd36f;
|
|
||||||
--ion-color-success-rgb: 45, 211, 111;
|
|
||||||
--ion-color-success-contrast: #ffffff;
|
|
||||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-success-shade: #28ba62;
|
|
||||||
--ion-color-success-tint: #42d77d;
|
|
||||||
|
|
||||||
/** warning **/
|
|
||||||
--ion-color-warning: #ffc409;
|
|
||||||
--ion-color-warning-rgb: 255, 196, 9;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-warning-shade: #e0ac08;
|
|
||||||
--ion-color-warning-tint: #ffca22;
|
|
||||||
|
|
||||||
/** danger **/
|
|
||||||
--ion-color-danger: #eb445a;
|
|
||||||
--ion-color-danger-rgb: 235, 68, 90;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-danger-shade: #cf3c4f;
|
|
||||||
--ion-color-danger-tint: #ed576b;
|
|
||||||
|
|
||||||
/** dark **/
|
|
||||||
--ion-color-dark: #222428;
|
|
||||||
--ion-color-dark-rgb: 34, 36, 40;
|
|
||||||
--ion-color-dark-contrast: #ffffff;
|
|
||||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-dark-shade: #1e2023;
|
|
||||||
--ion-color-dark-tint: #383a3e;
|
|
||||||
|
|
||||||
/** medium **/
|
|
||||||
--ion-color-medium: #92949c;
|
|
||||||
--ion-color-medium-rgb: 146, 148, 156;
|
|
||||||
--ion-color-medium-contrast: #ffffff;
|
|
||||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-medium-shade: #808289;
|
|
||||||
--ion-color-medium-tint: #9d9fa6;
|
|
||||||
|
|
||||||
/** light **/
|
|
||||||
--ion-color-light: #f4f5f8;
|
|
||||||
--ion-color-light-rgb: 244, 245, 248;
|
|
||||||
--ion-color-light-contrast: #000000;
|
|
||||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-light-shade: #d7d8da;
|
|
||||||
--ion-color-light-tint: #f5f6f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
/*
|
|
||||||
* Dark Colors
|
|
||||||
* -------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
--ion-color-primary: #428cff;
|
|
||||||
--ion-color-primary-rgb: 66,140,255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255,255,255;
|
|
||||||
--ion-color-primary-shade: #3a7be0;
|
|
||||||
--ion-color-primary-tint: #5598ff;
|
|
||||||
|
|
||||||
--ion-color-secondary: #50c8ff;
|
|
||||||
--ion-color-secondary-rgb: 80,200,255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255,255,255;
|
|
||||||
--ion-color-secondary-shade: #46b0e0;
|
|
||||||
--ion-color-secondary-tint: #62ceff;
|
|
||||||
|
|
||||||
--ion-color-tertiary: #6a64ff;
|
|
||||||
--ion-color-tertiary-rgb: 106,100,255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255,255,255;
|
|
||||||
--ion-color-tertiary-shade: #5d58e0;
|
|
||||||
--ion-color-tertiary-tint: #7974ff;
|
|
||||||
|
|
||||||
--ion-color-success: #2fdf75;
|
|
||||||
--ion-color-success-rgb: 47,223,117;
|
|
||||||
--ion-color-success-contrast: #000000;
|
|
||||||
--ion-color-success-contrast-rgb: 0,0,0;
|
|
||||||
--ion-color-success-shade: #29c467;
|
|
||||||
--ion-color-success-tint: #44e283;
|
|
||||||
|
|
||||||
--ion-color-warning: #ffd534;
|
|
||||||
--ion-color-warning-rgb: 255,213,52;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0,0,0;
|
|
||||||
--ion-color-warning-shade: #e0bb2e;
|
|
||||||
--ion-color-warning-tint: #ffd948;
|
|
||||||
|
|
||||||
--ion-color-danger: #ff4961;
|
|
||||||
--ion-color-danger-rgb: 255,73,97;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255,255,255;
|
|
||||||
--ion-color-danger-shade: #e04055;
|
|
||||||
--ion-color-danger-tint: #ff5b71;
|
|
||||||
|
|
||||||
--ion-color-dark: #f4f5f8;
|
|
||||||
--ion-color-dark-rgb: 244,245,248;
|
|
||||||
--ion-color-dark-contrast: #000000;
|
|
||||||
--ion-color-dark-contrast-rgb: 0,0,0;
|
|
||||||
--ion-color-dark-shade: #d7d8da;
|
|
||||||
--ion-color-dark-tint: #f5f6f9;
|
|
||||||
|
|
||||||
--ion-color-medium: #989aa2;
|
|
||||||
--ion-color-medium-rgb: 152,154,162;
|
|
||||||
--ion-color-medium-contrast: #000000;
|
|
||||||
--ion-color-medium-contrast-rgb: 0,0,0;
|
|
||||||
--ion-color-medium-shade: #86888f;
|
|
||||||
--ion-color-medium-tint: #a2a4ab;
|
|
||||||
|
|
||||||
--ion-color-light: #222428;
|
|
||||||
--ion-color-light-rgb: 34,36,40;
|
|
||||||
--ion-color-light-contrast: #ffffff;
|
|
||||||
--ion-color-light-contrast-rgb: 255,255,255;
|
|
||||||
--ion-color-light-shade: #1e2023;
|
|
||||||
--ion-color-light-tint: #383a3e;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Dark Theme
|
|
||||||
* -------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios body {
|
|
||||||
--ion-background-color: #000000;
|
|
||||||
--ion-background-color-rgb: 0,0,0;
|
|
||||||
|
|
||||||
--ion-text-color: #ffffff;
|
|
||||||
--ion-text-color-rgb: 255,255,255;
|
|
||||||
|
|
||||||
--ion-color-step-50: #0d0d0d;
|
|
||||||
--ion-color-step-100: #1a1a1a;
|
|
||||||
--ion-color-step-150: #262626;
|
|
||||||
--ion-color-step-200: #333333;
|
|
||||||
--ion-color-step-250: #404040;
|
|
||||||
--ion-color-step-300: #4d4d4d;
|
|
||||||
--ion-color-step-350: #595959;
|
|
||||||
--ion-color-step-400: #666666;
|
|
||||||
--ion-color-step-450: #737373;
|
|
||||||
--ion-color-step-500: #808080;
|
|
||||||
--ion-color-step-550: #8c8c8c;
|
|
||||||
--ion-color-step-600: #999999;
|
|
||||||
--ion-color-step-650: #a6a6a6;
|
|
||||||
--ion-color-step-700: #b3b3b3;
|
|
||||||
--ion-color-step-750: #bfbfbf;
|
|
||||||
--ion-color-step-800: #cccccc;
|
|
||||||
--ion-color-step-850: #d9d9d9;
|
|
||||||
--ion-color-step-900: #e6e6e6;
|
|
||||||
--ion-color-step-950: #f2f2f2;
|
|
||||||
|
|
||||||
--ion-item-background: #000000;
|
|
||||||
|
|
||||||
--ion-card-background: #1c1c1d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios ion-modal {
|
|
||||||
--ion-background-color: var(--ion-color-step-100);
|
|
||||||
--ion-toolbar-background: var(--ion-color-step-150);
|
|
||||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Material Design Dark Theme
|
|
||||||
* -------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
.md body {
|
|
||||||
--ion-background-color: #121212;
|
|
||||||
--ion-background-color-rgb: 18,18,18;
|
|
||||||
|
|
||||||
--ion-text-color: #ffffff;
|
|
||||||
--ion-text-color-rgb: 255,255,255;
|
|
||||||
|
|
||||||
--ion-border-color: #222222;
|
|
||||||
|
|
||||||
--ion-color-step-50: #1e1e1e;
|
|
||||||
--ion-color-step-100: #2a2a2a;
|
|
||||||
--ion-color-step-150: #363636;
|
|
||||||
--ion-color-step-200: #414141;
|
|
||||||
--ion-color-step-250: #4d4d4d;
|
|
||||||
--ion-color-step-300: #595959;
|
|
||||||
--ion-color-step-350: #656565;
|
|
||||||
--ion-color-step-400: #717171;
|
|
||||||
--ion-color-step-450: #7d7d7d;
|
|
||||||
--ion-color-step-500: #898989;
|
|
||||||
--ion-color-step-550: #949494;
|
|
||||||
--ion-color-step-600: #a0a0a0;
|
|
||||||
--ion-color-step-650: #acacac;
|
|
||||||
--ion-color-step-700: #b8b8b8;
|
|
||||||
--ion-color-step-750: #c4c4c4;
|
|
||||||
--ion-color-step-800: #d0d0d0;
|
|
||||||
--ion-color-step-850: #dbdbdb;
|
|
||||||
--ion-color-step-900: #e7e7e7;
|
|
||||||
--ion-color-step-950: #f3f3f3;
|
|
||||||
|
|
||||||
--ion-item-background: #1e1e1e;
|
|
||||||
|
|
||||||
--ion-toolbar-background: #1f1f1f;
|
|
||||||
|
|
||||||
--ion-tab-bar-background: #1f1f1f;
|
|
||||||
|
|
||||||
--ion-card-background: #1e1e1e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
import { IonCol, IonRouterLink, IonRow } from '@ionic/react';
|
||||||
|
|
||||||
|
interface ActionProps {
|
||||||
|
message: string;
|
||||||
|
text: string;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Action = (props: ActionProps): React.JSX.Element => (
|
||||||
|
<IonRow className="ion-text-center ion-justify-content-center">
|
||||||
|
<IonCol size="12">
|
||||||
|
<p>
|
||||||
|
{props.message}
|
||||||
|
<IonRouterLink className="custom-link" routerLink={props.link}>
|
||||||
|
{' '}
|
||||||
|
{props.text} →
|
||||||
|
</IonRouterLink>
|
||||||
|
</p>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
);
|
@@ -0,0 +1,29 @@
|
|||||||
|
.field:not(:last-child) {
|
||||||
|
margin-bottom: 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field {
|
||||||
|
ion-label {
|
||||||
|
padding-left: 0.2rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
color: #d3a6c7;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: rgb(236, 149, 35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.customInput {
|
||||||
|
--background: #834e76;
|
||||||
|
--padding-bottom: 1rem;
|
||||||
|
--padding-top: 1rem;
|
||||||
|
--padding-start: 1rem;
|
||||||
|
--padding-end: 1rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
transition: all 0.2s linear;
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
import { IonInput, IonLabel } from '@ionic/react';
|
||||||
|
import styles from './CustomField.module.scss';
|
||||||
|
|
||||||
|
interface FieldType {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
input: {
|
||||||
|
props: any;
|
||||||
|
state: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorType {
|
||||||
|
id: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CustomFieldProps {
|
||||||
|
field: FieldType;
|
||||||
|
errors?: ErrorType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomField = ({ field, errors }: CustomFieldProps): React.JSX.Element => {
|
||||||
|
const error = errors && errors.filter((e) => e.id === field.id)[0];
|
||||||
|
const errorMessage = error && errors.filter((e) => e.id === field.id)[0].message;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.field}>
|
||||||
|
<IonLabel className={styles.fieldLabel}>
|
||||||
|
{field.label}
|
||||||
|
{error && <p className="animate__animated animate__bounceIn">{errorMessage}</p>}
|
||||||
|
</IonLabel>
|
||||||
|
<IonInput className={styles.customInput} {...field.input.props} {...field.input.state} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomField;
|
@@ -0,0 +1,13 @@
|
|||||||
|
export const Wave = (): React.JSX.Element => (
|
||||||
|
<svg
|
||||||
|
style={{ marginBottom: '-0.5rem' }}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 1440 320"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#7a506f"
|
||||||
|
fill-opacity="1"
|
||||||
|
d="M0,288L40,277.3C80,267,160,245,240,224C320,203,400,181,480,176C560,171,640,181,720,181.3C800,181,880,171,960,144C1040,117,1120,75,1200,58.7C1280,43,1360,53,1400,58.7L1440,64L1440,320L1400,320C1360,320,1280,320,1200,320C1120,320,1040,320,960,320C880,320,800,320,720,320C640,320,560,320,480,320C400,320,320,320,240,320C160,320,80,320,40,320L0,320Z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
82
03_source/mobile/src/pages/DemoReactLogin/data/fields.tsx
Normal file
82
03_source/mobile/src/pages/DemoReactLogin/data/fields.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { useFormInput } from "./utils";
|
||||||
|
|
||||||
|
export const useSignupFields = () => {
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "name",
|
||||||
|
label: "Name",
|
||||||
|
required: true,
|
||||||
|
input: {
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
type: "text",
|
||||||
|
placeholder: "Joe Bloggs"
|
||||||
|
},
|
||||||
|
state: useFormInput("")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "email",
|
||||||
|
label: "Email",
|
||||||
|
required: true,
|
||||||
|
input: {
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
type: "email",
|
||||||
|
placeholder: "joe@bloggs.com"
|
||||||
|
},
|
||||||
|
state: useFormInput("")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "password",
|
||||||
|
label: "Password",
|
||||||
|
required: true,
|
||||||
|
input: {
|
||||||
|
|
||||||
|
props: {
|
||||||
|
|
||||||
|
type: "password",
|
||||||
|
placeholder: "*********"
|
||||||
|
},
|
||||||
|
state: useFormInput("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useLoginFields = () => {
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "email",
|
||||||
|
label: "Email",
|
||||||
|
required: true,
|
||||||
|
input: {
|
||||||
|
|
||||||
|
props: {
|
||||||
|
type: "email",
|
||||||
|
placeholder: "joe@bloggs.com"
|
||||||
|
},
|
||||||
|
state: useFormInput("")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "password",
|
||||||
|
label: "Password",
|
||||||
|
required: true,
|
||||||
|
input: {
|
||||||
|
|
||||||
|
props: {
|
||||||
|
type: "password",
|
||||||
|
placeholder: "*******"
|
||||||
|
},
|
||||||
|
state: useFormInput("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
46
03_source/mobile/src/pages/DemoReactLogin/data/utils.tsx
Normal file
46
03_source/mobile/src/pages/DemoReactLogin/data/utils.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export const useFormInput = (initialValue = "") => {
|
||||||
|
|
||||||
|
const [ value, setValue ] = useState(initialValue);
|
||||||
|
|
||||||
|
const handleChange = async e => {
|
||||||
|
|
||||||
|
const tempValue = await e.currentTarget.value;
|
||||||
|
setValue(tempValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
value,
|
||||||
|
reset: (newValue) => setValue(newValue),
|
||||||
|
onIonChange: handleChange,
|
||||||
|
onKeyUp: handleChange
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validateForm = fields => {
|
||||||
|
|
||||||
|
let errors = [];
|
||||||
|
|
||||||
|
fields.forEach(field => {
|
||||||
|
|
||||||
|
if (field.required) {
|
||||||
|
|
||||||
|
const fieldValue = field.input.state.value;
|
||||||
|
|
||||||
|
if (fieldValue === "") {
|
||||||
|
|
||||||
|
const error = {
|
||||||
|
|
||||||
|
id: field.id,
|
||||||
|
message: `Please check your ${ field.id }`,
|
||||||
|
};
|
||||||
|
|
||||||
|
errors.push(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
@@ -1,28 +1,33 @@
|
|||||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
import { IonRouterOutlet, IonTabs } from '@ionic/react';
|
||||||
|
|
||||||
import { cloudOutline, searchOutline } from 'ionicons/icons';
|
|
||||||
import { Route, Redirect } from 'react-router';
|
import { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
import './theme/variables.scss';
|
||||||
import Tab2 from './AppPages/Tab2';
|
|
||||||
|
|
||||||
import './style.scss';
|
import Home from './pages/Home';
|
||||||
|
import Login from './pages/Login';
|
||||||
|
import Signup from './pages/Signup';
|
||||||
|
|
||||||
function DemoReactLogin() {
|
function DemoReactLogin() {
|
||||||
return (
|
return (
|
||||||
<IonTabs>
|
<IonTabs>
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path="/demo-react-login/tab1">
|
<Route exact path="/demo-react-login/home">
|
||||||
<Tab1 />
|
<Home />
|
||||||
</Route>
|
|
||||||
<Route exact path="/demo-react-login/tab2">
|
|
||||||
<Tab2 />
|
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Redirect exact path="/demo-react-login" to="/demo-react-login/tab1" />
|
<Route exact path="/demo-react-login/signup">
|
||||||
|
<Signup />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route exact path="/demo-react-login/login">
|
||||||
|
<Login />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Redirect exact path="/demo-react-login" to="/demo-react-login/home" />
|
||||||
</IonRouterOutlet>
|
</IonRouterOutlet>
|
||||||
|
|
||||||
{/* */}
|
{/*
|
||||||
<IonTabBar slot="bottom">
|
<IonTabBar slot="bottom">
|
||||||
<IonTabButton tab="tab1" href="/demo-react-login/tab1">
|
<IonTabButton tab="tab1" href="/demo-react-login/tab1">
|
||||||
<IonIcon icon={cloudOutline} />
|
<IonIcon icon={cloudOutline} />
|
||||||
@@ -33,6 +38,7 @@ function DemoReactLogin() {
|
|||||||
<IonLabel>Search</IonLabel>
|
<IonLabel>Search</IonLabel>
|
||||||
</IonTabButton>
|
</IonTabButton>
|
||||||
</IonTabBar>
|
</IonTabBar>
|
||||||
|
*/}
|
||||||
</IonTabs>
|
</IonTabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
9
03_source/mobile/src/pages/DemoReactLogin/module.d.ts
vendored
Normal file
9
03_source/mobile/src/pages/DemoReactLogin/module.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
declare module '*.module.css' {
|
||||||
|
const classes: { readonly [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.module.scss' {
|
||||||
|
const classes: { readonly [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
.homePage {
|
||||||
|
ion-header {
|
||||||
|
ion-img {
|
||||||
|
border-bottom: 3px solid rgb(236, 149, 35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-footer {
|
||||||
|
background-color: #7c3b6a;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.getStarted {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #ffffff;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(#b8b8b8 1px, transparent 1px), radial-gradient(#b8b8b8 1px, #ffffff 1px);
|
||||||
|
background-size: 40px 40px;
|
||||||
|
background-position:
|
||||||
|
0 0,
|
||||||
|
20px 20px;
|
||||||
|
|
||||||
|
ion-card-title {
|
||||||
|
color: black !important;
|
||||||
|
letter-spacing: -0.08rem;
|
||||||
|
font-weight: 900 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
margin-top: 7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.getStartedButton {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
59
03_source/mobile/src/pages/DemoReactLogin/pages/Home.tsx
Normal file
59
03_source/mobile/src/pages/DemoReactLogin/pages/Home.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import {
|
||||||
|
IonButton,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonImg,
|
||||||
|
IonPage,
|
||||||
|
IonRouterLink,
|
||||||
|
IonRow,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import { Action } from '../components/Action';
|
||||||
|
import styles from './Home.module.scss';
|
||||||
|
|
||||||
|
const Home = (): React.JSX.Element => {
|
||||||
|
return (
|
||||||
|
<IonPage className={styles.homePage}>
|
||||||
|
<IonHeader>
|
||||||
|
{/* <IonToolbar className="ion-no-margin ion-no-padding"> */}
|
||||||
|
<IonImg src="/assets/DemoReactLogin/login2.jpeg" />
|
||||||
|
{/* </IonToolbar> */}
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<div className={styles.getStarted}>
|
||||||
|
<IonGrid>
|
||||||
|
<IonRow className={`ion-text-center ion-justify-content-center ${styles.heading}`}>
|
||||||
|
<IonCol size="11" className={styles.headingText}>
|
||||||
|
<IonCardTitle>
|
||||||
|
Join millions of other people discovering their creative side
|
||||||
|
</IonCardTitle>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className={`ion-text-center ion-justify-content-center`}>
|
||||||
|
<IonRouterLink routerLink="/signup" className="custom-link">
|
||||||
|
<IonCol size="11">
|
||||||
|
<IonButton className={`${styles.getStartedButton} custom-button`}>
|
||||||
|
Get started →
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRouterLink>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</div>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonGrid>
|
||||||
|
<Action message="Already got an account?" text="Login" link="/login" />
|
||||||
|
</IonGrid>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
@@ -0,0 +1,17 @@
|
|||||||
|
.loginPage {
|
||||||
|
ion-toolbar {
|
||||||
|
--border-style: none;
|
||||||
|
--border-color: transparent;
|
||||||
|
--padding-top: 1rem;
|
||||||
|
--padding-bottom: 1rem;
|
||||||
|
--padding-start: 1rem;
|
||||||
|
--padding-end: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.headingText {
|
||||||
|
h5 {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
color: #d3a6c7;
|
||||||
|
}
|
||||||
|
}
|
112
03_source/mobile/src/pages/DemoReactLogin/pages/Login.tsx
Normal file
112
03_source/mobile/src/pages/DemoReactLogin/pages/Login.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import {
|
||||||
|
IonBackButton,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonPage,
|
||||||
|
IonRouterLink,
|
||||||
|
IonRow,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import styles from './Login.module.scss';
|
||||||
|
|
||||||
|
import { arrowBack, shapesOutline } from 'ionicons/icons';
|
||||||
|
import CustomField from '../components/CustomField';
|
||||||
|
import { useLoginFields } from '../data/fields';
|
||||||
|
import { Action } from '../components/Action';
|
||||||
|
import { Wave } from '../components/Wave';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { validateForm } from '../data/utils';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
|
interface FieldType {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
input: {
|
||||||
|
props: any;
|
||||||
|
state: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorType {
|
||||||
|
id: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Login = (): React.JSX.Element => {
|
||||||
|
const params = useParams();
|
||||||
|
|
||||||
|
const fields: FieldType[] = useLoginFields();
|
||||||
|
const [errors, setErrors] = useState<ErrorType[] | false>(false);
|
||||||
|
|
||||||
|
const login = (): void => {
|
||||||
|
const errors = validateForm(fields);
|
||||||
|
setErrors(errors);
|
||||||
|
|
||||||
|
if (!errors.length) {
|
||||||
|
// Submit your form here
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
fields.forEach((field) => field.input.state.reset(''));
|
||||||
|
setErrors(false);
|
||||||
|
};
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage className={styles.loginPage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonBackButton icon={arrowBack} text="" className="custom-back" />
|
||||||
|
</IonButtons>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonButton className="custom-button">
|
||||||
|
<IonIcon icon={shapesOutline} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid className="ion-padding">
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12" className={styles.headingText}>
|
||||||
|
<IonCardTitle>Log in</IonCardTitle>
|
||||||
|
<h5>Welcome back, hope you're doing well</h5>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className="ion-margin-top ion-padding-top">
|
||||||
|
<IonCol size="12">
|
||||||
|
{fields.map((field) => {
|
||||||
|
return <CustomField field={field} errors={errors} />;
|
||||||
|
})}
|
||||||
|
|
||||||
|
<IonButton className="custom-button" expand="block" onClick={login}>
|
||||||
|
Login
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonGrid className="ion-no-margin ion-no-padding">
|
||||||
|
<Action message="Don't have an account?" text="Sign up" link="/signup" />
|
||||||
|
<Wave />
|
||||||
|
</IonGrid>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Login;
|
@@ -0,0 +1,17 @@
|
|||||||
|
.signupPage {
|
||||||
|
ion-toolbar {
|
||||||
|
--border-style: none;
|
||||||
|
--border-color: transparent;
|
||||||
|
--padding-top: 1rem;
|
||||||
|
--padding-bottom: 1rem;
|
||||||
|
--padding-start: 1rem;
|
||||||
|
--padding-end: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.headingText {
|
||||||
|
h5 {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
color: #d3a6c7;
|
||||||
|
}
|
||||||
|
}
|
111
03_source/mobile/src/pages/DemoReactLogin/pages/Signup.tsx
Normal file
111
03_source/mobile/src/pages/DemoReactLogin/pages/Signup.tsx
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
IonBackButton,
|
||||||
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
|
IonCardTitle,
|
||||||
|
IonCol,
|
||||||
|
IonContent,
|
||||||
|
IonFooter,
|
||||||
|
IonGrid,
|
||||||
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
|
IonPage,
|
||||||
|
IonRouterLink,
|
||||||
|
IonRow,
|
||||||
|
IonToolbar,
|
||||||
|
} from '@ionic/react';
|
||||||
|
import styles from './Signup.module.scss';
|
||||||
|
|
||||||
|
import { arrowBack, shapesOutline } from 'ionicons/icons';
|
||||||
|
import CustomField from '../components/CustomField';
|
||||||
|
import { useSignupFields } from '../data/fields';
|
||||||
|
import { Action } from '../components/Action';
|
||||||
|
import { Wave } from '../components/Wave';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { validateForm } from '../data/utils';
|
||||||
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
|
interface FieldType {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
input: {
|
||||||
|
props: any;
|
||||||
|
state: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorType {
|
||||||
|
id: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Signup = (): React.JSX.Element => {
|
||||||
|
const params = useParams();
|
||||||
|
const fields: FieldType[] = useSignupFields();
|
||||||
|
const [errors, setErrors] = useState<ErrorType[] | false>(false);
|
||||||
|
|
||||||
|
const createAccount = (): void => {
|
||||||
|
const errors = validateForm(fields);
|
||||||
|
setErrors(errors);
|
||||||
|
|
||||||
|
if (!errors.length) {
|
||||||
|
// Submit your form here
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
fields.forEach((field) => field.input.state.reset(''));
|
||||||
|
setErrors(false);
|
||||||
|
};
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonPage className={styles.signupPage}>
|
||||||
|
<IonHeader>
|
||||||
|
<IonToolbar>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonBackButton icon={arrowBack} text="" className="custom-back" />
|
||||||
|
</IonButtons>
|
||||||
|
|
||||||
|
<IonButtons slot="end">
|
||||||
|
<IonButton className="custom-button">
|
||||||
|
<IonIcon icon={shapesOutline} />
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
|
</IonToolbar>
|
||||||
|
</IonHeader>
|
||||||
|
<IonContent fullscreen>
|
||||||
|
<IonGrid className="ion-padding">
|
||||||
|
<IonRow>
|
||||||
|
<IonCol size="12" className={styles.headingText}>
|
||||||
|
<IonCardTitle>Sign up</IonCardTitle>
|
||||||
|
<h5>Lets get to know each other</h5>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
|
||||||
|
<IonRow className="ion-margin-top ion-padding-top">
|
||||||
|
<IonCol size="12">
|
||||||
|
{fields.map((field) => {
|
||||||
|
return <CustomField field={field} errors={errors} />;
|
||||||
|
})}
|
||||||
|
|
||||||
|
<IonButton className="custom-button" expand="block" onClick={createAccount}>
|
||||||
|
Create account
|
||||||
|
</IonButton>
|
||||||
|
</IonCol>
|
||||||
|
</IonRow>
|
||||||
|
</IonGrid>
|
||||||
|
</IonContent>
|
||||||
|
|
||||||
|
<IonFooter>
|
||||||
|
<IonGrid className="ion-no-margin ion-no-padding">
|
||||||
|
<Action message="Already got an account?" text="Login" link="/login" />
|
||||||
|
<Wave />
|
||||||
|
</IonGrid>
|
||||||
|
</IonFooter>
|
||||||
|
</IonPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Signup;
|
@@ -0,0 +1,19 @@
|
|||||||
|
import { Store } from "pullstate";
|
||||||
|
|
||||||
|
const AccountStore = new Store({
|
||||||
|
|
||||||
|
logged_in: false,
|
||||||
|
coffee_ids: []
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AccountStore;
|
||||||
|
|
||||||
|
// export const addToCart = (coffeeID) => {
|
||||||
|
|
||||||
|
// CartStore.update(s => { s.coffee_ids = [ ...s.coffee_ids, `${ parseInt(coffeeID) }` ]; });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export const removeFromCart = coffeeIndex => {
|
||||||
|
|
||||||
|
// CartStore.update(s => { s.coffee_ids.splice(coffeeIndex, 1) });
|
||||||
|
// }
|
@@ -0,0 +1,13 @@
|
|||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const getState = state => state;
|
||||||
|
|
||||||
|
// General getters
|
||||||
|
// export const getCoffees = createSelector(getState, state => state.coffees);
|
||||||
|
// export const getOffers = createSelector(getState, state => state.offers);
|
||||||
|
// export const getCoffeeSizes = createSelector(getState, state => state.sizes);
|
||||||
|
// export const getCartCoffees = createSelector(getState, state => state.coffee_ids);
|
||||||
|
// export const getFavouriteCoffees = createSelector(getState, state => state.coffee_ids);
|
||||||
|
|
||||||
|
// // More specific getters
|
||||||
|
// export const getCoffee = id => createSelector(getState, state => state.coffees.filter(c => parseInt(c.id) === parseInt(id))[0]);
|
@@ -0,0 +1 @@
|
|||||||
|
export { default as AccountStore } from "./AccountStore";
|
@@ -1,103 +0,0 @@
|
|||||||
#about-page {
|
|
||||||
ion-toolbar {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
--background: transparent;
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-toolbar ion-back-button,
|
|
||||||
ion-toolbar ion-button,
|
|
||||||
ion-toolbar ion-menu-button {
|
|
||||||
--color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .about-image {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
transition: opacity 500ms ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .madison {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/madison.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .austin {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/austin.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .chicago {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/chicago.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-header .seattle {
|
|
||||||
background-image: url('/assets/WeatherDemo/img/about/seattle.jpg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info {
|
|
||||||
position: relative;
|
|
||||||
margin-top: -10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--ion-background-color, #fff);
|
|
||||||
z-index: 2; // display rounded border above header image
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-list {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info p {
|
|
||||||
line-height: 130%;
|
|
||||||
|
|
||||||
color: var(--ion-color-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-info ion-icon {
|
|
||||||
margin-inline-end: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Only
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios .about-info {
|
|
||||||
--ion-padding: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios .about-info h3 {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#date-input-popover {
|
|
||||||
--offset-y: -var(--ion-safe-area-bottom);
|
|
||||||
|
|
||||||
--max-width: 90%;
|
|
||||||
--width: 336px;
|
|
||||||
}
|
|
||||||
|
@@ -12,7 +12,7 @@ import Info from './pages/Info';
|
|||||||
|
|
||||||
function DemoReactMarvelApp() {
|
function DemoReactMarvelApp() {
|
||||||
return (
|
return (
|
||||||
<IonTabs>
|
<IonTabs className="demo-react-marvel-app">
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path="/demo-react-marvel-app/home">
|
<Route exact path="/demo-react-marvel-app/home">
|
||||||
<Home />
|
<Home />
|
||||||
|
@@ -1,32 +1,30 @@
|
|||||||
.character-container {
|
.demo-react-marvel-app {
|
||||||
|
.character-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
.character-name-container {
|
.character-name-container {
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
position:absolute;
|
position: absolute;
|
||||||
z-index: 99999;
|
z-index: 99999;
|
||||||
background-color: rgba(0,0,0,0.7);
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
padding-right: 1rem;
|
padding-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.character-name-container ion-icon {
|
|
||||||
|
|
||||||
|
.character-name-container ion-icon {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.character-name-container ion-label {
|
|
||||||
|
|
||||||
|
.character-name-container ion-label {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
}
|
@@ -15,7 +15,7 @@ import {
|
|||||||
useIonRouter,
|
useIonRouter,
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
|
|
||||||
import './Home.css';
|
import './Home.scss';
|
||||||
import {
|
import {
|
||||||
addOutline,
|
addOutline,
|
||||||
chevronBackOutline,
|
chevronBackOutline,
|
||||||
|
@@ -1,59 +1,52 @@
|
|||||||
.characterContainer {
|
.characterContainer {
|
||||||
|
position: relative;
|
||||||
position: relative;
|
text-align: center;
|
||||||
text-align: center;
|
color: white;
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-item {
|
ion-item {
|
||||||
--padding-start: 0;
|
--padding-start: 0;
|
||||||
--inner-padding-end: 0;
|
--inner-padding-end: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-label {
|
ion-label {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.characterNameContainer {
|
.characterNameContainer {
|
||||||
|
display: flex;
|
||||||
display: flex;
|
flex-direction: row;
|
||||||
flex-direction: row;
|
justify-content: center;
|
||||||
justify-content: center;
|
position: absolute;
|
||||||
position: absolute;
|
z-index: 99999;
|
||||||
z-index: 99999;
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
bottom: 0;
|
||||||
bottom: 0;
|
width: 100%;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.characterNameContainer ion-label {
|
.characterNameContainer ion-label {
|
||||||
|
font-size: 1rem !important;
|
||||||
font-size: 1rem !important;
|
font-weight: 500;
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.characterStats {
|
.characterStats {
|
||||||
|
text-align: center;
|
||||||
text-align: center;
|
margin-top: 1.5rem;
|
||||||
margin-top: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.characterStat {
|
.characterStat {
|
||||||
|
background-color: var(--ion-color-primary);
|
||||||
background-color: var(--ion-color-primary);
|
padding: 1rem;
|
||||||
padding: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.characterStat ion-card-title {
|
.characterStat ion-card-title {
|
||||||
|
font-size: 1rem;
|
||||||
font-size: 1rem;
|
--color: white;
|
||||||
--color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.characterStat ion-card-subtitle {
|
.characterStat ion-card-subtitle {
|
||||||
|
font-size: 1rem;
|
||||||
font-size: 1rem;
|
--color: white;
|
||||||
--color: white;
|
}
|
||||||
}
|
|
||||||
|
@@ -1,245 +0,0 @@
|
|||||||
.demo-react-marvel-app {
|
|
||||||
/* Ionic Variables and Theming. For more info, please see:
|
|
||||||
http://ionicframework.com/docs/theming/ */
|
|
||||||
|
|
||||||
/** Ionic CSS Variables **/
|
|
||||||
:root {
|
|
||||||
/** primary **/
|
|
||||||
--ion-color-primary: #3880ff;
|
|
||||||
--ion-color-primary-rgb: 56, 128, 255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-primary-shade: #3171e0;
|
|
||||||
--ion-color-primary-tint: #4c8dff;
|
|
||||||
|
|
||||||
/** secondary **/
|
|
||||||
--ion-color-secondary: #3dc2ff;
|
|
||||||
--ion-color-secondary-rgb: 61, 194, 255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-secondary-shade: #36abe0;
|
|
||||||
--ion-color-secondary-tint: #50c8ff;
|
|
||||||
|
|
||||||
/** tertiary **/
|
|
||||||
--ion-color-tertiary: #5260ff;
|
|
||||||
--ion-color-tertiary-rgb: 82, 96, 255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-tertiary-shade: #4854e0;
|
|
||||||
--ion-color-tertiary-tint: #6370ff;
|
|
||||||
|
|
||||||
/** success **/
|
|
||||||
--ion-color-success: #2dd36f;
|
|
||||||
--ion-color-success-rgb: 45, 211, 111;
|
|
||||||
--ion-color-success-contrast: #ffffff;
|
|
||||||
--ion-color-success-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-success-shade: #28ba62;
|
|
||||||
--ion-color-success-tint: #42d77d;
|
|
||||||
|
|
||||||
/** warning **/
|
|
||||||
--ion-color-warning: #ffc409;
|
|
||||||
--ion-color-warning-rgb: 255, 196, 9;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-warning-shade: #e0ac08;
|
|
||||||
--ion-color-warning-tint: #ffca22;
|
|
||||||
|
|
||||||
/** danger **/
|
|
||||||
--ion-color-danger: #eb445a;
|
|
||||||
--ion-color-danger-rgb: 235, 68, 90;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-danger-shade: #cf3c4f;
|
|
||||||
--ion-color-danger-tint: #ed576b;
|
|
||||||
|
|
||||||
/** dark **/
|
|
||||||
--ion-color-dark: #222428;
|
|
||||||
--ion-color-dark-rgb: 34, 36, 40;
|
|
||||||
--ion-color-dark-contrast: #ffffff;
|
|
||||||
--ion-color-dark-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-dark-shade: #1e2023;
|
|
||||||
--ion-color-dark-tint: #383a3e;
|
|
||||||
|
|
||||||
/** medium **/
|
|
||||||
--ion-color-medium: #92949c;
|
|
||||||
--ion-color-medium-rgb: 146, 148, 156;
|
|
||||||
--ion-color-medium-contrast: #ffffff;
|
|
||||||
--ion-color-medium-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-medium-shade: #808289;
|
|
||||||
--ion-color-medium-tint: #9d9fa6;
|
|
||||||
|
|
||||||
/** light **/
|
|
||||||
--ion-color-light: #f4f5f8;
|
|
||||||
--ion-color-light-rgb: 244, 245, 248;
|
|
||||||
--ion-color-light-contrast: #000000;
|
|
||||||
--ion-color-light-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-light-shade: #d7d8da;
|
|
||||||
--ion-color-light-tint: #f5f6f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @media (prefers-color-scheme: dark) { */
|
|
||||||
/*
|
|
||||||
* Dark Colors
|
|
||||||
* -------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
--ion-color-primary: #ee0000;
|
|
||||||
--ion-color-primary-rgb: 66, 140, 255;
|
|
||||||
--ion-color-primary-contrast: #ffffff;
|
|
||||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-primary-shade: #3a7be0;
|
|
||||||
--ion-color-primary-tint: #5598ff;
|
|
||||||
|
|
||||||
--ion-color-secondary: #50c8ff;
|
|
||||||
--ion-color-secondary-rgb: 80, 200, 255;
|
|
||||||
--ion-color-secondary-contrast: #ffffff;
|
|
||||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-secondary-shade: #46b0e0;
|
|
||||||
--ion-color-secondary-tint: #62ceff;
|
|
||||||
|
|
||||||
--ion-color-tertiary: #6a64ff;
|
|
||||||
--ion-color-tertiary-rgb: 106, 100, 255;
|
|
||||||
--ion-color-tertiary-contrast: #ffffff;
|
|
||||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-tertiary-shade: #5d58e0;
|
|
||||||
--ion-color-tertiary-tint: #7974ff;
|
|
||||||
|
|
||||||
--ion-color-success: #2fdf75;
|
|
||||||
--ion-color-success-rgb: 47, 223, 117;
|
|
||||||
--ion-color-success-contrast: #000000;
|
|
||||||
--ion-color-success-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-success-shade: #29c467;
|
|
||||||
--ion-color-success-tint: #44e283;
|
|
||||||
|
|
||||||
--ion-color-warning: #ffd534;
|
|
||||||
--ion-color-warning-rgb: 255, 213, 52;
|
|
||||||
--ion-color-warning-contrast: #000000;
|
|
||||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-warning-shade: #e0bb2e;
|
|
||||||
--ion-color-warning-tint: #ffd948;
|
|
||||||
|
|
||||||
--ion-color-danger: #ee0000;
|
|
||||||
--ion-color-danger-rgb: 255, 73, 97;
|
|
||||||
--ion-color-danger-contrast: #ffffff;
|
|
||||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-danger-shade: #e04055;
|
|
||||||
--ion-color-danger-tint: #ff5b71;
|
|
||||||
|
|
||||||
--ion-color-dark: #f4f5f8;
|
|
||||||
--ion-color-dark-rgb: 244, 245, 248;
|
|
||||||
--ion-color-dark-contrast: #000000;
|
|
||||||
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-dark-shade: #d7d8da;
|
|
||||||
--ion-color-dark-tint: #f5f6f9;
|
|
||||||
|
|
||||||
--ion-color-medium: #989aa2;
|
|
||||||
--ion-color-medium-rgb: 152, 154, 162;
|
|
||||||
--ion-color-medium-contrast: #000000;
|
|
||||||
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
|
||||||
--ion-color-medium-shade: #86888f;
|
|
||||||
--ion-color-medium-tint: #a2a4ab;
|
|
||||||
|
|
||||||
--ion-color-light: #222428;
|
|
||||||
--ion-color-light-rgb: 34, 36, 40;
|
|
||||||
--ion-color-light-contrast: #ffffff;
|
|
||||||
--ion-color-light-contrast-rgb: 255, 255, 255;
|
|
||||||
--ion-color-light-shade: #1e2023;
|
|
||||||
--ion-color-light-tint: #383a3e;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* iOS Dark Theme
|
|
||||||
* -------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ios body {
|
|
||||||
--ion-background-color: #000000;
|
|
||||||
--ion-background-color-rgb: 0, 0, 0;
|
|
||||||
|
|
||||||
--ion-text-color: #ffffff;
|
|
||||||
--ion-text-color-rgb: 255, 255, 255;
|
|
||||||
|
|
||||||
--ion-color-step-50: #0d0d0d;
|
|
||||||
--ion-color-step-100: #1a1a1a;
|
|
||||||
--ion-color-step-150: #262626;
|
|
||||||
--ion-color-step-200: #333333;
|
|
||||||
--ion-color-step-250: #404040;
|
|
||||||
--ion-color-step-300: #4d4d4d;
|
|
||||||
--ion-color-step-350: #595959;
|
|
||||||
--ion-color-step-400: #666666;
|
|
||||||
--ion-color-step-450: #737373;
|
|
||||||
--ion-color-step-500: #808080;
|
|
||||||
--ion-color-step-550: #8c8c8c;
|
|
||||||
--ion-color-step-600: #999999;
|
|
||||||
--ion-color-step-650: #a6a6a6;
|
|
||||||
--ion-color-step-700: #b3b3b3;
|
|
||||||
--ion-color-step-750: #bfbfbf;
|
|
||||||
--ion-color-step-800: #cccccc;
|
|
||||||
--ion-color-step-850: #d9d9d9;
|
|
||||||
--ion-color-step-900: #e6e6e6;
|
|
||||||
--ion-color-step-950: #f2f2f2;
|
|
||||||
|
|
||||||
--ion-item-background: #000000;
|
|
||||||
|
|
||||||
--ion-card-background: #1c1c1d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ios ion-modal {
|
|
||||||
--ion-background-color: var(--ion-color-step-100);
|
|
||||||
--ion-toolbar-background: var(--ion-color-step-150);
|
|
||||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Material Design Dark Theme
|
|
||||||
* -------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
.md body {
|
|
||||||
--ion-background-color: #121212;
|
|
||||||
--ion-background-color-rgb: 18, 18, 18;
|
|
||||||
|
|
||||||
--ion-text-color: #ffffff;
|
|
||||||
--ion-text-color-rgb: 255, 255, 255;
|
|
||||||
|
|
||||||
--ion-border-color: #222222;
|
|
||||||
|
|
||||||
--ion-color-step-50: #1e1e1e;
|
|
||||||
--ion-color-step-100: #2a2a2a;
|
|
||||||
--ion-color-step-150: #363636;
|
|
||||||
--ion-color-step-200: #414141;
|
|
||||||
--ion-color-step-250: #4d4d4d;
|
|
||||||
--ion-color-step-300: #595959;
|
|
||||||
--ion-color-step-350: #656565;
|
|
||||||
--ion-color-step-400: #717171;
|
|
||||||
--ion-color-step-450: #7d7d7d;
|
|
||||||
--ion-color-step-500: #898989;
|
|
||||||
--ion-color-step-550: #949494;
|
|
||||||
--ion-color-step-600: #a0a0a0;
|
|
||||||
--ion-color-step-650: #acacac;
|
|
||||||
--ion-color-step-700: #b8b8b8;
|
|
||||||
--ion-color-step-750: #c4c4c4;
|
|
||||||
--ion-color-step-800: #d0d0d0;
|
|
||||||
--ion-color-step-850: #dbdbdb;
|
|
||||||
--ion-color-step-900: #e7e7e7;
|
|
||||||
--ion-color-step-950: #f3f3f3;
|
|
||||||
|
|
||||||
--ion-item-background: #1e1e1e;
|
|
||||||
|
|
||||||
--ion-toolbar-background: #1f1f1f;
|
|
||||||
|
|
||||||
--ion-tab-bar-background: #1f1f1f;
|
|
||||||
|
|
||||||
--ion-card-background: #1e1e1e;
|
|
||||||
}
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
.non-link {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,29 +1,43 @@
|
|||||||
import {
|
import {
|
||||||
IonButton,
|
IonButton,
|
||||||
|
IonButtons,
|
||||||
IonCardSubtitle,
|
IonCardSubtitle,
|
||||||
IonCardTitle,
|
IonCardTitle,
|
||||||
IonCol,
|
IonCol,
|
||||||
IonContent,
|
IonContent,
|
||||||
IonGrid,
|
IonGrid,
|
||||||
IonHeader,
|
IonHeader,
|
||||||
|
IonIcon,
|
||||||
IonPage,
|
IonPage,
|
||||||
IonRow,
|
IonRow,
|
||||||
IonText,
|
IonText,
|
||||||
IonTitle,
|
IonTitle,
|
||||||
IonToolbar,
|
IonToolbar,
|
||||||
|
useIonRouter,
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
import { useGetSelectedTheme } from '../store/ThemeStore';
|
import { useGetSelectedTheme } from '../store/ThemeStore';
|
||||||
import './Info.scss';
|
import './Info.scss';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { chevronBackOutline } from 'ionicons/icons';
|
||||||
|
|
||||||
function Info(): React.JSX.Element {
|
function Info(): React.JSX.Element {
|
||||||
const currentTheme = useGetSelectedTheme();
|
const currentTheme = useGetSelectedTheme();
|
||||||
|
|
||||||
|
const router = useIonRouter();
|
||||||
|
function handleBackClick() {
|
||||||
|
router.goBack();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IonPage>
|
<IonPage>
|
||||||
<IonHeader>
|
<IonHeader>
|
||||||
<IonToolbar>
|
<IonToolbar>
|
||||||
<IonTitle>Info TS</IonTitle>
|
<IonTitle>Info TS</IonTitle>
|
||||||
|
<IonButtons slot="start">
|
||||||
|
<IonButton shape="round" onClick={handleBackClick}>
|
||||||
|
<IonIcon icon={chevronBackOutline}></IonIcon>
|
||||||
|
</IonButton>
|
||||||
|
</IonButtons>
|
||||||
</IonToolbar>
|
</IonToolbar>
|
||||||
</IonHeader>
|
</IonHeader>
|
||||||
<IonContent fullscreen>
|
<IonContent fullscreen>
|
||||||
|
@@ -14,9 +14,9 @@ import {
|
|||||||
|
|
||||||
import { Geolocation } from '@capacitor/geolocation';
|
import { Geolocation } from '@capacitor/geolocation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { SkeletonDashboard } from '../components/SkeletonDashboard';
|
import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard';
|
||||||
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
|
import { chevronBackOutline, refreshOutline } from 'ionicons/icons';
|
||||||
import { CurrentWeather } from '../components/CurrentWeather';
|
import { CurrentWeather } from '../TestComponents/CurrentWeather';
|
||||||
|
|
||||||
function Tab1() {
|
function Tab1() {
|
||||||
const router = useIonRouter();
|
const router = useIonRouter();
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
IonToolbar,
|
IonToolbar,
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CurrentWeather } from '../components/CurrentWeather';
|
import { CurrentWeather } from '../TestComponents/CurrentWeather';
|
||||||
|
|
||||||
function Tab2() {
|
function Tab2() {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
|
@@ -0,0 +1,29 @@
|
|||||||
|
import { IonImg, IonItem, IonLabel, IonList, IonLoading, IonThumbnail } from '@ionic/react';
|
||||||
|
import { SkeletonPosts } from './SkeletonPosts';
|
||||||
|
|
||||||
|
export const Posts = ({ posts, useSkeleton }): React.JSX.Element => (
|
||||||
|
<>
|
||||||
|
{posts.length > 0 ? (
|
||||||
|
<IonList>
|
||||||
|
{posts.map((post, index) => {
|
||||||
|
return (
|
||||||
|
<IonItem key={index}>
|
||||||
|
<IonThumbnail slot="start">
|
||||||
|
<IonImg src={post.image} />
|
||||||
|
</IonThumbnail>
|
||||||
|
<IonLabel className="">
|
||||||
|
<h3>{post.title}</h3>
|
||||||
|
<p>{post.blurb}</p>
|
||||||
|
<p>{post.date}</p>
|
||||||
|
</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</IonList>
|
||||||
|
) : useSkeleton ? (
|
||||||
|
<SkeletonPosts />
|
||||||
|
) : (
|
||||||
|
<IonLoading isOpen={true} spinner="bubbles" message="Loading posts..." />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
@@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
IonItem,
|
||||||
|
IonLabel,
|
||||||
|
IonList,
|
||||||
|
IonSkeletonText,
|
||||||
|
IonThumbnail,
|
||||||
|
} from "@ionic/react";
|
||||||
|
|
||||||
|
export const SkeletonPosts = (): JSX.Element => {
|
||||||
|
const postAmount = 10;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IonList>
|
||||||
|
{[...Array(postAmount)].map((post, index) => {
|
||||||
|
return (
|
||||||
|
<IonItem key={index}>
|
||||||
|
<IonThumbnail slot="start">
|
||||||
|
<IonSkeletonText animated />
|
||||||
|
</IonThumbnail>
|
||||||
|
<IonLabel>
|
||||||
|
<h3>
|
||||||
|
<IonSkeletonText animated style={{ width: "50%" }} />
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
<IonSkeletonText animated style={{ width: "100%" }} />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<IonSkeletonText animated style={{ width: "30%" }} />
|
||||||
|
</p>
|
||||||
|
</IonLabel>
|
||||||
|
</IonItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</IonList>
|
||||||
|
);
|
||||||
|
};
|
72
03_source/mobile/src/pages/DemoSkeletonText/data/index.ts
Normal file
72
03_source/mobile/src/pages/DemoSkeletonText/data/index.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
export const fakePosts = [
|
||||||
|
{
|
||||||
|
title: 'Sed ut perspiciatis unde',
|
||||||
|
blurb:
|
||||||
|
'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/1.png',
|
||||||
|
date: '01/04/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'But I must explain to you',
|
||||||
|
blurb:
|
||||||
|
'But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/2.png',
|
||||||
|
date: '23/02/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Far far away, behind the word',
|
||||||
|
blurb:
|
||||||
|
'Far far away, behind the word mountains, far from the countries Vokalia and Consonantia, there live the blind texts. Separated they live in Bookmarksgrove right at the coast of the Semantics, a large language ocean.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/3.png',
|
||||||
|
date: '18/02/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'A wonderful serenity',
|
||||||
|
blurb:
|
||||||
|
'A wonderful serenity has taken possession of my entire soul, like these sweet mornings of spring which I enjoy with my whole heart. I am alone, and feel the charm of existence in this spot.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/4.png',
|
||||||
|
date: '09/02/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Morning troubled dreams',
|
||||||
|
blurb:
|
||||||
|
'One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/5.png',
|
||||||
|
date: '01/02/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'The quick brown fox',
|
||||||
|
blurb:
|
||||||
|
'The quick, brown fox jumps over a lazy dog. DJs flock by when MTV ax quiz prog. Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/6.png',
|
||||||
|
date: '14/01/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Lorem ipsum dolor',
|
||||||
|
blurb:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/7.png',
|
||||||
|
date: '04/01/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Lorem ipsum dolor',
|
||||||
|
blurb:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/8.png',
|
||||||
|
date: '04/01/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Lorem ipsum dolor',
|
||||||
|
blurb:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/9.png',
|
||||||
|
date: '04/01/2021',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Lorem ipsum dolor',
|
||||||
|
blurb:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes.',
|
||||||
|
image: '/assets/DemoSkeletonText/scenery/10.png',
|
||||||
|
date: '04/01/2021',
|
||||||
|
},
|
||||||
|
];
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user