Compare commits
17 Commits
develop/mo
...
develop/mo
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ed95621b2f | ||
![]() |
2258fd8fb9 | ||
![]() |
0f674badd9 | ||
![]() |
bc731ea2b8 | ||
![]() |
c11dba6297 | ||
![]() |
b9fbe4e476 | ||
![]() |
0d844eed3f | ||
![]() |
2862cddb6b | ||
![]() |
55509b483c | ||
![]() |
3f2ac2a285 | ||
![]() |
1216de4ff9 | ||
![]() |
7966d8abf5 | ||
![]() |
e975aebfcd | ||
![]() |
7f6970b183 | ||
![]() |
e83854ed2a | ||
![]() |
6961f058df | ||
![]() |
b515337acc |
@@ -2,3 +2,8 @@ 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;
|
||||
}
|
||||
|
@@ -56,6 +56,12 @@ const Home = () => {
|
||||
<IonToolbar>
|
||||
<IonTitle>Popular</IonTitle>
|
||||
|
||||
{/*
|
||||
const router = useIonRouter();
|
||||
function handleBackClick() {
|
||||
router.goBack();
|
||||
}
|
||||
*/}
|
||||
<IonButtons slot="start">
|
||||
<IonButton onClick={() => handleBackClick()}>
|
||||
<IonIcon icon={chevronBackOutline} color="primary" />
|
||||
|
@@ -49,42 +49,67 @@ import {
|
||||
apps,
|
||||
appsOutline,
|
||||
book,
|
||||
bookOutline,
|
||||
brushOutline,
|
||||
calculatorOutline,
|
||||
car,
|
||||
cart,
|
||||
cartOutline,
|
||||
cashOutline,
|
||||
chatbubbleEllipses,
|
||||
chatbubbleEllipsesOutline,
|
||||
chatbubbleOutline,
|
||||
chevronBackOutline,
|
||||
chevronForward,
|
||||
chevronForwardOutline,
|
||||
cloudOutline,
|
||||
codeSlashOutline,
|
||||
codeWorkingOutline,
|
||||
colorPaletteOutline,
|
||||
createOutline,
|
||||
document,
|
||||
documentTextOutline,
|
||||
filmOutline,
|
||||
flashOutline,
|
||||
gift,
|
||||
giftOutline,
|
||||
globeSharp,
|
||||
gridOutline,
|
||||
heart,
|
||||
imageOutline,
|
||||
imagesOutline,
|
||||
keyOutline,
|
||||
languageOutline,
|
||||
layers,
|
||||
layersOutline,
|
||||
list,
|
||||
listCircle,
|
||||
listOutline,
|
||||
logInOutline,
|
||||
logoFacebook,
|
||||
mapOutline,
|
||||
menuOutline,
|
||||
paperPlaneOutline,
|
||||
people,
|
||||
person,
|
||||
personCircleOutline,
|
||||
personOutline,
|
||||
pizzaOutline,
|
||||
qrCodeOutline,
|
||||
refreshOutline,
|
||||
restaurant,
|
||||
restaurantOutline,
|
||||
settingsOutline,
|
||||
shareSocialOutline,
|
||||
statsChart,
|
||||
sunny,
|
||||
swapHorizontal,
|
||||
trashOutline,
|
||||
walkOutline,
|
||||
} from 'ionicons/icons';
|
||||
import AboutPopover from '../../components/AboutPopover';
|
||||
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';
|
||||
|
||||
interface OwnProps {}
|
||||
@@ -102,7 +127,13 @@ interface 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 [showPopover, setShowPopover] = useState(false);
|
||||
const [popoverEvent, setPopoverEvent] = useState<MouseEvent>();
|
||||
@@ -122,23 +153,23 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
}
|
||||
|
||||
function handleLanguageClick() {
|
||||
router.push(paths.CHANGE_LANGUAGE);
|
||||
router.push(PATHS.CHANGE_LANGUAGE);
|
||||
}
|
||||
|
||||
function handleNotImplementedClick() {
|
||||
router.push(paths.NOT_IMPLEMENTED);
|
||||
router.push(PATHS.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
function handleDemoPageClick() {
|
||||
router.push(paths.DEMO_PAGE);
|
||||
router.push(PATHS.DEMO_PAGE);
|
||||
}
|
||||
|
||||
function handleServiceAgreementClick() {
|
||||
router.push(paths.SERVICE_AGREEMENT);
|
||||
router.push(PATHS.SERVICE_AGREEMENT);
|
||||
}
|
||||
|
||||
function handlePrivacyAgreementClick() {
|
||||
router.push(paths.PRIVACY_AGREEMENT);
|
||||
router.push(PATHS.PRIVACY_AGREEMENT);
|
||||
}
|
||||
|
||||
const [showLogoutConfirmModal, setShowLogoutConfirmModal] = useState<boolean>(false);
|
||||
@@ -158,12 +189,8 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
setShowLogoutConfirmModal(false);
|
||||
}
|
||||
|
||||
function handleDemoWeatherApp() {
|
||||
router.push(paths.DEMO_WEATHER_APP);
|
||||
}
|
||||
|
||||
function handleDemoReactShopClick() {
|
||||
router.push(paths.DEMO_REACT_SHOP);
|
||||
router.push(PATHS.DEMO_REACT_SHOP);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -192,263 +219,29 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonHeader>
|
||||
|
||||
<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>
|
||||
<IonLabel>Weather App</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>
|
||||
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_2FA_EXAMPLE, 'forward')}>
|
||||
<IonIcon slot="start" icon={keyOutline} size="large"></IonIcon>
|
||||
<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>
|
||||
<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</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')):
|
||||
<IonItem
|
||||
button={true}
|
||||
onClick={() => router.push(PATHS.DEMO_REACT_THEME_SWITCHER, 'forward')}
|
||||
>
|
||||
<IonIcon slot="start" icon={colorPaletteOutline} size="large"></IonIcon>
|
||||
<IonLabel>Demo React Theme Switcher</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -456,15 +249,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<IonList inset={false}>
|
||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_REACT_WHATSAPP_CLONE, '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')):
|
||||
<IonItem button={true} onClick={() => router.push(PATHS.DEMO_SKELETON_TEXT, 'forward')}>
|
||||
<IonIcon slot="start" icon={codeWorkingOutline} size="large"></IonIcon>
|
||||
<IonLabel>Demo Skeleton Text</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -472,7 +257,10 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Sticky Bottom Sheet Example</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -480,15 +268,21 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Storage Example</IonLabel>
|
||||
<IonLabel>
|
||||
Demo Storage Example{' '}
|
||||
<span style={{ fontWeight: 'bold' }}>(need fix, message cannot display)</span>
|
||||
</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo SwiperJS Tutorial</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -496,19 +290,241 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<IonList inset={false}>
|
||||
<IonItem button={true} onClick(() => router.push(paths.DEMO_WEATHER_APP_UI, 'forward')):
|
||||
<IonIcon slot="start" icon={sunnyOutline} size="large"></IonIcon>
|
||||
<IonLabel>Demo Weather App UI</IonLabel>
|
||||
<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_2FA_EXAMPLE, 'forward')}>
|
||||
<IonIcon slot="start" icon={keyOutline} size="large"></IonIcon>
|
||||
<IonLabel>Demo 2FA Example</IonLabel>
|
||||
<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 <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>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
@@ -526,27 +542,35 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
*/}
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo React Overlay Hooks</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -554,20 +578,28 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
</IonItem>
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
</IonItem>
|
||||
@@ -585,7 +617,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
<IonItem
|
||||
button={true}
|
||||
onClick={() => {
|
||||
router.push(paths.DEMO_CLUB_HOUSE, 'forward');
|
||||
router.push(PATHS.DEMO_CLUB_HOUSE, 'forward');
|
||||
}}
|
||||
>
|
||||
<IonIcon slot="start" icon={cart} size="large"></IonIcon>
|
||||
@@ -598,7 +630,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
<IonItem
|
||||
button={true}
|
||||
onClick={() => {
|
||||
router.push(paths.DEMO_SCORE_BOARD, 'forward');
|
||||
router.push(PATHS.DEMO_SCORE_BOARD, 'forward');
|
||||
}}
|
||||
>
|
||||
<IonIcon slot="start" icon={car} size="large"></IonIcon>
|
||||
@@ -611,7 +643,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Quote App</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -619,7 +651,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Qr scanner</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -627,7 +659,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Shop App UI</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -635,7 +667,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Dictionary App</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -643,7 +675,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Recipe App</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -651,7 +683,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Sliding Profile</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -659,7 +691,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Quiz App</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -667,7 +699,7 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>Demo Blog Post UI</IonLabel>
|
||||
<IonIcon icon={chevronForwardOutline}></IonIcon>
|
||||
@@ -675,7 +707,10 @@ const SettingsPage: React.FC<SettingsProps> = ({ speakers, speakerSessions, logo
|
||||
</IonList>
|
||||
|
||||
<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>
|
||||
<IonLabel>
|
||||
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>
|
||||
</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>
|
||||
|
||||
{/* 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>
|
||||
|
||||
{/* 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
|
||||
className="ion-padding"
|
||||
style={{
|
||||
@@ -752,5 +857,5 @@ export default connect<OwnProps, StateProps, DispatchProps>({
|
||||
setAccessToken,
|
||||
setIsLoggedIn,
|
||||
},
|
||||
component: React.memo(SettingsPage),
|
||||
component: React.memo(DemoList),
|
||||
});
|
||||
|
@@ -51,8 +51,8 @@ import { CompletedCard } from '../components/CompletedCard';
|
||||
import { QuizStats } from '../components/QuizStats';
|
||||
|
||||
const Questions = () => {
|
||||
const mainContainerRef = useRef();
|
||||
const completionContainerRef = useRef();
|
||||
const mainContainerRef = useRef(null);
|
||||
const completionContainerRef = useRef(null);
|
||||
const swiperRef = useRef(null);
|
||||
|
||||
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 Tab1 from './AppPages/Tab1';
|
||||
import Tab2 from './AppPages/Tab2';
|
||||
|
||||
import './style.scss';
|
||||
import './theme/variables.scss';
|
||||
import Home from './pages/Home';
|
||||
import Movies from './pages/Movies';
|
||||
|
||||
function DemoReactItemList() {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet>
|
||||
<Route exact path="/demo-react-item-list/tab1">
|
||||
<Tab1 />
|
||||
<Route exact path="/demo-react-item-list/home">
|
||||
<Home />
|
||||
</Route>
|
||||
<Route exact path="/demo-react-item-list/tab2">
|
||||
<Tab2 />
|
||||
<Route exact path="/demo-react-item-list/movies">
|
||||
<Movies />
|
||||
</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>
|
||||
|
||||
{/* */}
|
||||
{/*
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="tab1" href="/demo-react-item-list/tab1">
|
||||
<IonIcon icon={cloudOutline} />
|
||||
@@ -33,6 +31,7 @@ function DemoReactItemList() {
|
||||
<IonLabel>Search</IonLabel>
|
||||
</IonTabButton>
|
||||
</IonTabBar>
|
||||
*/}
|
||||
</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 Tab1 from './AppPages/Tab1';
|
||||
import Tab2 from './AppPages/Tab2';
|
||||
|
||||
import './style.scss';
|
||||
import Page from './pages/Page';
|
||||
import Page2 from './pages/Page2';
|
||||
|
||||
|
@@ -26,7 +26,7 @@ const Page: React.FC = () => {
|
||||
}
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonHeader className="ion-no-border">
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<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 Tab1 from './AppPages/Tab1';
|
||||
import Tab2 from './AppPages/Tab2';
|
||||
import './theme/variables.scss';
|
||||
|
||||
import './style.scss';
|
||||
import Home from './pages/Home';
|
||||
import Login from './pages/Login';
|
||||
import Signup from './pages/Signup';
|
||||
|
||||
function DemoReactLogin() {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet>
|
||||
<Route exact path="/demo-react-login/tab1">
|
||||
<Tab1 />
|
||||
</Route>
|
||||
<Route exact path="/demo-react-login/tab2">
|
||||
<Tab2 />
|
||||
<Route exact path="/demo-react-login/home">
|
||||
<Home />
|
||||
</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>
|
||||
|
||||
{/* */}
|
||||
{/*
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="tab1" href="/demo-react-login/tab1">
|
||||
<IonIcon icon={cloudOutline} />
|
||||
@@ -33,6 +38,7 @@ function DemoReactLogin() {
|
||||
<IonLabel>Search</IonLabel>
|
||||
</IonTabButton>
|
||||
</IonTabBar>
|
||||
*/}
|
||||
</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() {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonTabs className="demo-react-marvel-app">
|
||||
<IonRouterOutlet>
|
||||
<Route exact path="/demo-react-marvel-app/home">
|
||||
<Home />
|
||||
|
@@ -1,32 +1,30 @@
|
||||
.character-container {
|
||||
|
||||
.demo-react-marvel-app {
|
||||
.character-container {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
.character-name-container {
|
||||
|
||||
}
|
||||
.character-name-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
position:absolute;
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
background-color: rgba(0,0,0,0.7);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.character-name-container ion-icon {
|
||||
}
|
||||
|
||||
.character-name-container ion-icon {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.character-name-container ion-label {
|
||||
}
|
||||
|
||||
.character-name-container ion-label {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,7 +15,7 @@ import {
|
||||
useIonRouter,
|
||||
} from '@ionic/react';
|
||||
|
||||
import './Home.css';
|
||||
import './Home.scss';
|
||||
import {
|
||||
addOutline,
|
||||
chevronBackOutline,
|
||||
|
@@ -1,59 +1,52 @@
|
||||
.characterContainer {
|
||||
|
||||
position: relative;
|
||||
text-align: center;
|
||||
color: white;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
ion-item {
|
||||
--padding-start: 0;
|
||||
--inner-padding-end: 0;
|
||||
--padding-start: 0;
|
||||
--inner-padding-end: 0;
|
||||
}
|
||||
|
||||
ion-label {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.characterNameContainer {
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.characterNameContainer ion-label {
|
||||
|
||||
font-size: 1rem !important;
|
||||
font-weight: 500;
|
||||
font-size: 1rem !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.characterStats {
|
||||
|
||||
text-align: center;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.characterStat {
|
||||
|
||||
background-color: var(--ion-color-primary);
|
||||
padding: 1rem;
|
||||
background-color: var(--ion-color-primary);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.characterStat ion-card-title {
|
||||
|
||||
font-size: 1rem;
|
||||
--color: white;
|
||||
font-size: 1rem;
|
||||
--color: white;
|
||||
}
|
||||
|
||||
.characterStat ion-card-subtitle {
|
||||
|
||||
font-size: 1rem;
|
||||
--color: white;
|
||||
}
|
||||
font-size: 1rem;
|
||||
--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;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,39 @@
|
||||
import { IonCardSubtitle, IonCardTitle, IonCol, IonInfiniteScroll, IonInfiniteScrollContent, IonList, IonRow } from "@ionic/react";
|
||||
import { connectInfiniteHits } from "react-instantsearch-core";
|
||||
import CustomSearchHit from "./CustomSearchHit";
|
||||
|
||||
const CustomInfiniteHits = ({ hits, hasMore, refineNext }) => {
|
||||
|
||||
const getMore = (e, refine) => {
|
||||
|
||||
refine();
|
||||
e.target.complete();
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<div className="ais-InfiniteHits">
|
||||
<IonList className="ais-InfiniteHits-list">
|
||||
<div className="ais-Hits__root">
|
||||
|
||||
<IonRow>
|
||||
{ hits.map(hit => <CustomSearchHit key={ hit.objectID } hit={ hit } />) }
|
||||
|
||||
{ hits.length < 1 &&
|
||||
<IonCol size="12" className="ion-text-center ion-padding-top ion-margin-top">
|
||||
<IonCardTitle>No results found</IonCardTitle>
|
||||
<IonCardSubtitle>Try something else</IonCardSubtitle>
|
||||
</IonCol>
|
||||
}
|
||||
</IonRow>
|
||||
|
||||
<IonInfiniteScroll threshold="100px" onIonInfinite={ e => getMore(e, refineNext) }>
|
||||
<IonInfiniteScrollContent loadingSpinner="bubbles" loadingText="Getting more movies..." />
|
||||
</IonInfiniteScroll>
|
||||
</div>
|
||||
</IonList>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connectInfiniteHits(CustomInfiniteHits);
|
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonCol,
|
||||
IonInfiniteScroll,
|
||||
IonInfiniteScrollContent,
|
||||
IonList,
|
||||
IonRow,
|
||||
} from '@ionic/react';
|
||||
import { connectInfiniteHits } from 'react-instantsearch-core';
|
||||
import CustomSearchHit from './CustomSearchHit';
|
||||
|
||||
const CustomInfiniteHits = ({ hits, hasMore, refineNext }): React.JSX.Element => {
|
||||
const getMore = (e, refine) => {
|
||||
refine();
|
||||
e.target.complete();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ais-InfiniteHits">
|
||||
<IonList className="ais-InfiniteHits-list">
|
||||
<div className="ais-Hits__root">
|
||||
<IonRow>
|
||||
{hits.map((hit) => (
|
||||
<CustomSearchHit key={hit.objectID} hit={hit} />
|
||||
))}
|
||||
|
||||
{hits.length < 1 && (
|
||||
<IonCol size="12" className="ion-text-center ion-padding-top ion-margin-top">
|
||||
<IonCardTitle>No results found</IonCardTitle>
|
||||
<IonCardSubtitle>Try something else</IonCardSubtitle>
|
||||
</IonCol>
|
||||
)}
|
||||
</IonRow>
|
||||
|
||||
<IonInfiniteScroll threshold="100px" onIonInfinite={(e) => getMore(e, refineNext)}>
|
||||
<IonInfiniteScrollContent
|
||||
loadingSpinner="bubbles"
|
||||
loadingText="Getting more movies..."
|
||||
/>
|
||||
</IonInfiniteScroll>
|
||||
</div>
|
||||
</IonList>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default connectInfiniteHits(CustomInfiniteHits);
|
@@ -0,0 +1,29 @@
|
||||
import { IonCol, IonRouterLink } from "@ionic/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import styles from "../pages/Movies.module.scss";
|
||||
|
||||
const CustomSearchHit = ({ hit }) => {
|
||||
|
||||
// Any movies without an image, lets just exclude them
|
||||
// So the UI looks nicer.
|
||||
// When I scraped the data, I put a placeholder URL image in
|
||||
// But after more thought, it looks cleaner without including these.
|
||||
if (hit && hit.image !== "https://critics.io/img/movies/poster-placeholder.png" && hit.backdrop_path !== null) {
|
||||
return (
|
||||
|
||||
<IonCol size="6" className={ styles.movie }>
|
||||
<Link to={{ pathname: `/movie/${ hit.objectID }`, state: { movie: hit } }} className="non-link">
|
||||
<div className={ styles.movieInfo }>
|
||||
<img src={ hit.image } alt="movie poster" />
|
||||
<h2>{ hit.title }</h2>
|
||||
</div>
|
||||
</Link>
|
||||
</IonCol>
|
||||
);
|
||||
} else {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomSearchHit;
|
@@ -0,0 +1,33 @@
|
||||
import { IonCol, IonRouterLink } from '@ionic/react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styles from '../pages/Movies.module.scss';
|
||||
|
||||
const CustomSearchHit = ({ hit }): React.JSX.Element | null => {
|
||||
// Any movies without an image, lets just exclude them
|
||||
// So the UI looks nicer.
|
||||
// When I scraped the data, I put a placeholder URL image in
|
||||
// But after more thought, it looks cleaner without including these.
|
||||
if (
|
||||
hit &&
|
||||
hit.image !== 'https://critics.io/img/movies/poster-placeholder.png' &&
|
||||
hit.backdrop_path !== null
|
||||
) {
|
||||
return (
|
||||
<IonCol size="6" className={styles.movie}>
|
||||
<Link
|
||||
to={{ pathname: `/movie/${hit.objectID}`, state: { movie: hit } }}
|
||||
className="non-link"
|
||||
>
|
||||
<div className={styles.movieInfo}>
|
||||
<img src={hit.image} alt="movie poster" />
|
||||
<h2>{hit.title}</h2>
|
||||
</div>
|
||||
</Link>
|
||||
</IonCol>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default CustomSearchHit;
|
@@ -0,0 +1,9 @@
|
||||
import { IonSearchbar } from "@ionic/react";
|
||||
import { searchCircleOutline } from "ionicons/icons";
|
||||
import { connectSearchBox } from "react-instantsearch-core";
|
||||
|
||||
const CustomSearchBox = ({ currentRefinement, refine }) => (
|
||||
<IonSearchbar animated={ true } onIonCancel={ () => refine('') } value={ currentRefinement } onIonChange={ event => refine(event.currentTarget.value) } onKeyUp={ event => refine(event.currentTarget.value) } placeholder="Try 'Avengers'" icon={ searchCircleOutline } slot="end" />
|
||||
);
|
||||
|
||||
export default connectSearchBox(CustomSearchBox);
|
@@ -0,0 +1,18 @@
|
||||
import { IonSearchbar } from '@ionic/react';
|
||||
import { searchCircleOutline } from 'ionicons/icons';
|
||||
import { connectSearchBox } from 'react-instantsearch-core';
|
||||
|
||||
const CustomSearchBox = ({ currentRefinement, refine }): React.JSX.Element => (
|
||||
<IonSearchbar
|
||||
animated={true}
|
||||
onIonCancel={() => refine('')}
|
||||
value={currentRefinement}
|
||||
onIonChange={(event) => refine(event.currentTarget.value)}
|
||||
onKeyUp={(event) => refine(event.currentTarget.value)}
|
||||
placeholder="Try 'Avengers'"
|
||||
icon={searchCircleOutline}
|
||||
slot="end"
|
||||
/>
|
||||
);
|
||||
|
||||
export default connectSearchBox(CustomSearchBox);
|
@@ -6,7 +6,8 @@ import { Route, Redirect } from 'react-router';
|
||||
import Tab1 from './AppPages/Tab1';
|
||||
import Tab2 from './AppPages/Tab2';
|
||||
|
||||
import './style.scss';
|
||||
// import './style.scss';
|
||||
import './theme/variables.scss';
|
||||
|
||||
function DemoReactMovieAppWithAlgolia() {
|
||||
return (
|
||||
@@ -19,7 +20,11 @@ function DemoReactMovieAppWithAlgolia() {
|
||||
<Tab2 />
|
||||
</Route>
|
||||
|
||||
<Redirect exact path="/demo-react-movie-app-with-algolia" to="/demo-react-movie-app-with-algolia/tab1" />
|
||||
<Redirect
|
||||
exact
|
||||
path="/demo-react-movie-app-with-algolia"
|
||||
to="/demo-react-movie-app-with-algolia/tab1"
|
||||
/>
|
||||
</IonRouterOutlet>
|
||||
|
||||
{/* */}
|
||||
|
@@ -0,0 +1,86 @@
|
||||
import { IonBackButton, IonBadge, IonButton, IonCardSubtitle, IonCardTitle, IonCol, IonContent, IonGrid, IonHeader, IonPage, IonRow } from '@ionic/react';
|
||||
import styles from './Movie.module.scss';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { arrowBack } from 'ionicons/icons';
|
||||
import { useLocation, useParams } from 'react-router';
|
||||
|
||||
const Movie = () => {
|
||||
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const [ movie, setMovie ] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (location.state.movie) {
|
||||
|
||||
const backdropPath = `https://image.tmdb.org/t/p/w500${ location.state.movie.backdrop_path }`;
|
||||
location.state.movie.cover_image = backdropPath;
|
||||
|
||||
setMovie(location.state.movie);
|
||||
}
|
||||
}, [ params ]);
|
||||
|
||||
const parseDate = dateToParse => {
|
||||
|
||||
const dateParts = dateToParse.split("-");
|
||||
return `${ dateParts[2] }-${ dateParts[1] }-${ dateParts[0] }`;
|
||||
}
|
||||
|
||||
return (
|
||||
<IonPage className={ styles.page }>
|
||||
<IonHeader>
|
||||
<img src={ movie.cover_image } alt="movie cover" />
|
||||
<IonBackButton color="light" className={ styles.backButton } text=" Back to search" icon={ arrowBack } />
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol size="5">
|
||||
<img src={ movie.image } />
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="7" className={ styles.movieInfo }>
|
||||
<IonCardSubtitle>Title</IonCardSubtitle>
|
||||
<IonCardTitle>{ movie.title }</IonCardTitle>
|
||||
|
||||
<IonRow className="ion-justify-content-center ion-margin-top">
|
||||
<IonCol size="6">
|
||||
<IonCardSubtitle>Popularity</IonCardSubtitle>
|
||||
<IonBadge className={ styles.infoBadge }>{ movie.popularity ? movie.popularity : "Unknown" }</IonBadge>
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="6">
|
||||
<IonCardSubtitle>Release Date</IonCardSubtitle>
|
||||
<IonBadge className={ styles.infoBadge }>{ movie.release_date ? parseDate(movie.release_date) : "Unknown" }</IonBadge>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow>
|
||||
<IonCol size="12">
|
||||
<IonCardSubtitle>Search for Youtube Trailer</IonCardSubtitle>
|
||||
<a href={ `https://www.youtube.com/results?search_query=${ movie.title } trailer` } rel="noopener" target="_new">
|
||||
<IonButton>
|
||||
Search now
|
||||
</IonButton>
|
||||
</a>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow className={ styles.movieOverview }>
|
||||
<IonCol size="12">
|
||||
<IonCardSubtitle>Overview</IonCardSubtitle>
|
||||
<p>{ movie.overview ? movie.overview : `No overview to show unfortunately. Try doing a google search for '${ movie.title }.` }</p>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Movie;
|
@@ -0,0 +1,54 @@
|
||||
.page {
|
||||
|
||||
ion-header {
|
||||
|
||||
background-color: #373b5e;
|
||||
}
|
||||
|
||||
ion-toolbar {
|
||||
|
||||
--border-style: none;
|
||||
--background: #373b5e;
|
||||
--color: white;
|
||||
--min-height: 8rem;
|
||||
--stripe: #373b5e;
|
||||
--bg: #3e4368;
|
||||
--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-title {
|
||||
|
||||
--color: white;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.movieOverview {
|
||||
|
||||
background-color:#373b5e;
|
||||
padding: 2rem;
|
||||
color: white;
|
||||
|
||||
ion-card-subtitle {
|
||||
|
||||
--color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.backButton {
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.infoBadge {
|
||||
|
||||
background-color:rgba(44, 53, 105, 0.8);
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
import {
|
||||
IonBackButton,
|
||||
IonBadge,
|
||||
IonButton,
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonCol,
|
||||
IonContent,
|
||||
IonGrid,
|
||||
IonHeader,
|
||||
IonPage,
|
||||
IonRow,
|
||||
} from '@ionic/react';
|
||||
import styles from './Movie.module.scss';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { arrowBack } from 'ionicons/icons';
|
||||
import { useLocation, useParams } from 'react-router';
|
||||
|
||||
const Movie = (): React.JSX.Element => {
|
||||
const params = useParams();
|
||||
const location = useLocation();
|
||||
const [movie, setMovie] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.state.movie) {
|
||||
const backdropPath = `https://image.tmdb.org/t/p/w500${location.state.movie.backdrop_path}`;
|
||||
location.state.movie.cover_image = backdropPath;
|
||||
|
||||
setMovie(location.state.movie);
|
||||
}
|
||||
}, [params]);
|
||||
|
||||
const parseDate = (dateToParse) => {
|
||||
const dateParts = dateToParse.split('-');
|
||||
return `${dateParts[2]}-${dateParts[1]}-${dateParts[0]}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage className={styles.page}>
|
||||
<IonHeader>
|
||||
<img src={movie.cover_image} alt="movie cover" />
|
||||
<IonBackButton
|
||||
color="light"
|
||||
className={styles.backButton}
|
||||
text=" Back to search"
|
||||
icon={arrowBack}
|
||||
/>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol size="5">
|
||||
<img src={movie.image} />
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="7" className={styles.movieInfo}>
|
||||
<IonCardSubtitle>Title</IonCardSubtitle>
|
||||
<IonCardTitle>{movie.title}</IonCardTitle>
|
||||
|
||||
<IonRow className="ion-justify-content-center ion-margin-top">
|
||||
<IonCol size="6">
|
||||
<IonCardSubtitle>Popularity</IonCardSubtitle>
|
||||
<IonBadge className={styles.infoBadge}>
|
||||
{movie.popularity ? movie.popularity : 'Unknown'}
|
||||
</IonBadge>
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="6">
|
||||
<IonCardSubtitle>Release Date</IonCardSubtitle>
|
||||
<IonBadge className={styles.infoBadge}>
|
||||
{movie.release_date ? parseDate(movie.release_date) : 'Unknown'}
|
||||
</IonBadge>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow>
|
||||
<IonCol size="12">
|
||||
<IonCardSubtitle>Search for Youtube Trailer</IonCardSubtitle>
|
||||
<a
|
||||
href={`https://www.youtube.com/results?search_query=${movie.title} trailer`}
|
||||
rel="noopener"
|
||||
target="_new"
|
||||
>
|
||||
<IonButton>Search now</IonButton>
|
||||
</a>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow className={styles.movieOverview}>
|
||||
<IonCol size="12">
|
||||
<IonCardSubtitle>Overview</IonCardSubtitle>
|
||||
<p>
|
||||
{movie.overview
|
||||
? movie.overview
|
||||
: `No overview to show unfortunately. Try doing a google search for '${movie.title}.`}
|
||||
</p>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Movie;
|
@@ -0,0 +1,94 @@
|
||||
import { IonButton, IonCardSubtitle, IonCardTitle, IonContent, IonHeader, IonIcon, IonPage, IonToolbar } from '@ionic/react';
|
||||
import styles from './Movies.module.scss';
|
||||
|
||||
// Algolia imports
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
import { InstantSearch } from 'react-instantsearch-dom';
|
||||
|
||||
// Custom Algolia UI
|
||||
import CustomSearchbox from '../components/CustomSearchbox';
|
||||
import CustomInfiniteHits from '../components/CustomInfiniteHits';
|
||||
import { useEffect } from 'react';
|
||||
import { addOutline } from 'ionicons/icons';
|
||||
|
||||
const searchClient = algoliasearch('QZKBD6VPU7', 'db21b77f5f3bf4d4cbde385b7f33c60d');
|
||||
|
||||
const Movies = props => {
|
||||
|
||||
// PWA functionality for a custom add to homescreen
|
||||
// This allows us to display a custom button based on service worker
|
||||
useEffect(() => {
|
||||
|
||||
const buttInstall = document.getElementById('buttInstall');
|
||||
window.addEventListener('beforeinstallprompt', (event) => {
|
||||
|
||||
console.log('👍', 'beforeinstallprompt', event);
|
||||
|
||||
// Save the event so it can be triggered later.
|
||||
window.deferredPrompt = event;
|
||||
buttInstall.classList.toggle('hidden', false);
|
||||
});
|
||||
|
||||
window.addEventListener('appinstalled', (event) => {
|
||||
|
||||
console.log('👍', 'appinstalled', event);
|
||||
// Clear the deferredPrompt so it can be garbage collected
|
||||
window.deferredPrompt = null;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const addToHomeScreen = async () => {
|
||||
|
||||
const buttInstall = document.getElementById('buttInstall');
|
||||
|
||||
console.log('👍', 'buttInstall-clicked');
|
||||
const promptEvent = window.deferredPrompt;
|
||||
|
||||
if (!promptEvent) {
|
||||
|
||||
// The deferred prompt isn't available.
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the install prompt.
|
||||
promptEvent.prompt();
|
||||
|
||||
// Log the result
|
||||
const result = await promptEvent.userChoice;
|
||||
console.log('👍', 'userChoice', result);
|
||||
|
||||
// Reset the deferred prompt variable, since
|
||||
// prompt() can only be called once.
|
||||
window.deferredPrompt = null;
|
||||
|
||||
// Hide the install button.
|
||||
buttInstall.classList.toggle('hidden', true);
|
||||
}
|
||||
|
||||
return (
|
||||
<IonPage className={ styles.page }>
|
||||
|
||||
<InstantSearch searchClient={ searchClient } indexName="dev_movies">
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<div className={ styles.searchContainer }>
|
||||
<IonCardTitle>Movie List</IonCardTitle>
|
||||
<IonCardSubtitle>with algolia search</IonCardSubtitle>
|
||||
<CustomSearchbox />
|
||||
|
||||
<IonButton id="buttInstall" color="success" fill="solid" size="small" className="hidden add-button" onClick={ () => addToHomeScreen() }>
|
||||
<IonIcon icon={ addOutline } /> Install App
|
||||
</IonButton>
|
||||
</div>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<CustomInfiniteHits />
|
||||
</IonContent>
|
||||
</InstantSearch>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Movies;
|
@@ -0,0 +1,133 @@
|
||||
.page {
|
||||
|
||||
ion-header {
|
||||
|
||||
background-color: #373b5e;
|
||||
}
|
||||
|
||||
ion-toolbar {
|
||||
|
||||
--border-style: none;
|
||||
--background: #373b5e;
|
||||
--color: white;
|
||||
--min-height: 8rem;
|
||||
--stripe: #373b5e;
|
||||
--bg: #3e4368;
|
||||
--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;
|
||||
}
|
||||
|
||||
.searchContainer {
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: white;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
|
||||
ion-card-title,
|
||||
ion-card-subtitle {
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ion-card-title {
|
||||
|
||||
font-size: 1.5rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
ion-card-subtitle {
|
||||
|
||||
color: rgb(189, 189, 189);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-searchbar {
|
||||
|
||||
--border-radius: 10px;
|
||||
--background: white;
|
||||
--color: black;
|
||||
padding: 0.5rem !important;
|
||||
margin: 0 !important;
|
||||
--background: #2a2d44;
|
||||
--color: white;
|
||||
--icon-color: white;
|
||||
--clear-button-color: white;
|
||||
}
|
||||
|
||||
ion-searchbar {
|
||||
|
||||
input{
|
||||
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
ion-list {
|
||||
|
||||
background-color: #e7edfb;
|
||||
}
|
||||
}
|
||||
|
||||
.movie {
|
||||
|
||||
.movieInfo {
|
||||
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
|
||||
img {
|
||||
|
||||
height: 75vmin;
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background-color: red;
|
||||
width: 95%;
|
||||
background-color: rgba(44, 53, 105, 0.8);
|
||||
color: white;
|
||||
font-size: 4vmin;
|
||||
padding: 1rem;
|
||||
min-height: 4.5rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
max-height: 4.5rem;
|
||||
margin-bottom: 0;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
IonButton,
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonContent,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonPage,
|
||||
IonToolbar,
|
||||
} from '@ionic/react';
|
||||
import styles from './Movies.module.scss';
|
||||
|
||||
// Algolia imports
|
||||
import algoliasearch from 'algoliasearch/lite';
|
||||
import { InstantSearch } from 'react-instantsearch-dom';
|
||||
|
||||
// Custom Algolia UI
|
||||
import CustomSearchbox from '../components/CustomSearchbox';
|
||||
import CustomInfiniteHits from '../components/CustomInfiniteHits';
|
||||
import { useEffect } from 'react';
|
||||
import { addOutline } from 'ionicons/icons';
|
||||
|
||||
const searchClient = algoliasearch('QZKBD6VPU7', 'db21b77f5f3bf4d4cbde385b7f33c60d');
|
||||
|
||||
const Movies = (props: any): React.JSX.Element => {
|
||||
// PWA functionality for a custom add to homescreen
|
||||
// This allows us to display a custom button based on service worker
|
||||
useEffect(() => {
|
||||
const buttInstall = document.getElementById('buttInstall');
|
||||
window.addEventListener('beforeinstallprompt', (event) => {
|
||||
console.log('👍', 'beforeinstallprompt', event);
|
||||
|
||||
// Save the event so it can be triggered later.
|
||||
window.deferredPrompt = event;
|
||||
buttInstall.classList.toggle('hidden', false);
|
||||
});
|
||||
|
||||
window.addEventListener('appinstalled', (event) => {
|
||||
console.log('👍', 'appinstalled', event);
|
||||
// Clear the deferredPrompt so it can be garbage collected
|
||||
window.deferredPrompt = null;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const addToHomeScreen = async () => {
|
||||
const buttInstall = document.getElementById('buttInstall');
|
||||
|
||||
console.log('👍', 'buttInstall-clicked');
|
||||
const promptEvent = window.deferredPrompt;
|
||||
|
||||
if (!promptEvent) {
|
||||
// The deferred prompt isn't available.
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the install prompt.
|
||||
promptEvent.prompt();
|
||||
|
||||
// Log the result
|
||||
const result = await promptEvent.userChoice;
|
||||
console.log('👍', 'userChoice', result);
|
||||
|
||||
// Reset the deferred prompt variable, since
|
||||
// prompt() can only be called once.
|
||||
window.deferredPrompt = null;
|
||||
|
||||
// Hide the install button.
|
||||
buttInstall.classList.toggle('hidden', true);
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage className={styles.page}>
|
||||
<InstantSearch searchClient={searchClient} indexName="dev_movies">
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<div className={styles.searchContainer}>
|
||||
<IonCardTitle>Movie List</IonCardTitle>
|
||||
<IonCardSubtitle>with algolia search</IonCardSubtitle>
|
||||
<CustomSearchbox />
|
||||
|
||||
<IonButton
|
||||
id="buttInstall"
|
||||
color="success"
|
||||
fill="solid"
|
||||
size="small"
|
||||
className="hidden add-button"
|
||||
onClick={() => addToHomeScreen()}
|
||||
>
|
||||
<IonIcon icon={addOutline} />
|
||||
Install App
|
||||
</IonButton>
|
||||
</div>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<CustomInfiniteHits />
|
||||
</IonContent>
|
||||
</InstantSearch>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Movies;
|
@@ -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;
|
||||
}
|
||||
|
@@ -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,21 @@
|
||||
import './ExploreContainer.css';
|
||||
|
||||
const ExploreContainer = (): React.JSX.Element => {
|
||||
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,38 +1,27 @@
|
||||
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 Tab1 from './AppPages/Tab1';
|
||||
import Tab2 from './AppPages/Tab2';
|
||||
// import Tab1 from './AppPages/Tab1';
|
||||
// import Tab2 from './AppPages/Tab2';
|
||||
|
||||
import './style.scss';
|
||||
import './theme/variables.scss';
|
||||
import Home from './pages/Home';
|
||||
import Add from './pages/Add';
|
||||
|
||||
function DemoReactNotes() {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonTabs className="demo-react-notes">
|
||||
<IonRouterOutlet>
|
||||
<Route exact path="/demo-react-notes/tab1">
|
||||
<Tab1 />
|
||||
<Route exact path="/demo-react-notes/home">
|
||||
<Home />
|
||||
</Route>
|
||||
<Route exact path="/demo-react-notes/tab2">
|
||||
<Tab2 />
|
||||
<Route exact path="/demo-react-notes/add">
|
||||
<Add />
|
||||
</Route>
|
||||
|
||||
<Redirect exact path="/demo-react-notes" to="/demo-react-notes/tab1" />
|
||||
<Redirect exact path="/demo-react-notes" to="/demo-react-notes/home" />
|
||||
</IonRouterOutlet>
|
||||
|
||||
{/* */}
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="tab1" href="/demo-react-notes/tab1">
|
||||
<IonIcon icon={cloudOutline} />
|
||||
<IonLabel>Dashboard</IonLabel>
|
||||
</IonTabButton>
|
||||
<IonTabButton tab="tab2" href="/demo-react-notes/tab2">
|
||||
<IonIcon icon={searchOutline} />
|
||||
<IonLabel>Search</IonLabel>
|
||||
</IonTabButton>
|
||||
</IonTabBar>
|
||||
</IonTabs>
|
||||
);
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
.title {
|
||||
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.customInput {
|
||||
|
||||
border-radius: 22px !important;
|
||||
--padding-bottom: 1rem;
|
||||
--padding-top: 1rem;
|
||||
|
||||
box-shadow: 0 0.7px 0.9px rgba(0, 0, 0, 0.101),
|
||||
0 0.9px 2.5px rgba(0, 0, 0, 0.145),
|
||||
0 2.5px 6px rgba(0, 0, 0, 0.189),
|
||||
0 8px 20px rgba(0, 0, 0, 0.29);
|
||||
}
|
||||
|
||||
.customInput {
|
||||
|
||||
margin-bottom: 1rem !important;
|
||||
}
|
||||
|
||||
.saveButton {
|
||||
|
||||
--border-radius: 12px !important;
|
||||
|
||||
--padding-top: 1.75rem !important;
|
||||
--padding-bottom: 1.75rem !important;
|
||||
}
|
115
03_source/mobile/src/pages/DemoReactNotes/pages/Add.tsx
Normal file
115
03_source/mobile/src/pages/DemoReactNotes/pages/Add.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import {
|
||||
IonBackButton,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonCol,
|
||||
IonContent,
|
||||
IonGrid,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonPage,
|
||||
IonRow,
|
||||
IonSelect,
|
||||
IonSelectOption,
|
||||
IonTextarea,
|
||||
IonTitle,
|
||||
IonToolbar,
|
||||
useIonRouter,
|
||||
} from '@ionic/react';
|
||||
import styles from './Add.module.scss';
|
||||
|
||||
import { checkmarkOutline } from 'ionicons/icons';
|
||||
import { getCategories, getNotes } from '../store/Selectors';
|
||||
import { CategoryStore, NoteStore } from '../store';
|
||||
import { addNote } from '../store/NoteStore';
|
||||
import { useState } from 'react';
|
||||
|
||||
const Add = (): React.JSX.Element => {
|
||||
const categories = CategoryStore.useState(getCategories);
|
||||
const notes = NoteStore.useState(getNotes);
|
||||
const [noteCategory, setNoteCategory] = useState<number | false>(false);
|
||||
const [noteContent, setNoteContent] = useState<string>('');
|
||||
const router = useIonRouter();
|
||||
|
||||
const add = () => {
|
||||
const note = {
|
||||
id: notes.length + 1,
|
||||
category_id: noteCategory,
|
||||
note: noteContent,
|
||||
complete: false,
|
||||
};
|
||||
|
||||
addNote(note);
|
||||
router.goBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton />
|
||||
</IonButtons>
|
||||
|
||||
<IonTitle className={styles.title}>Add note</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent fullscreen>
|
||||
<IonGrid className="ion-padding-top">
|
||||
<IonRow>
|
||||
<IonCol size="12" className="ion-padding-start">
|
||||
<h1 className={styles.mainTitle}>Add a note</h1>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow>
|
||||
<IonCol size="12">
|
||||
<IonItem lines="none" className={styles.customInput}>
|
||||
<IonLabel position="floating">Category</IonLabel>
|
||||
<IonSelect
|
||||
placeholder="Select..."
|
||||
value={noteCategory}
|
||||
onIonChange={(e) => setNoteCategory(e.target.value)}
|
||||
>
|
||||
{categories.map((category) => {
|
||||
return (
|
||||
<IonSelectOption value={category.id} key={category.id}>
|
||||
{category.name}
|
||||
</IonSelectOption>
|
||||
);
|
||||
})}
|
||||
</IonSelect>
|
||||
</IonItem>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow>
|
||||
<IonCol size="12">
|
||||
<IonItem lines="none" className={styles.customInput}>
|
||||
<IonLabel position="floating">Note</IonLabel>
|
||||
<IonTextarea
|
||||
value={noteContent}
|
||||
onIonChange={(e) => setNoteContent(e.target.value)}
|
||||
placeholder="Enter note text here..."
|
||||
/>
|
||||
</IonItem>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow>
|
||||
<IonCol size="12">
|
||||
<IonButton expand="block" className={styles.saveButton} onClick={add}>
|
||||
<IonIcon icon={checkmarkOutline} />
|
||||
Save note
|
||||
</IonButton>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Add;
|
130
03_source/mobile/src/pages/DemoReactNotes/pages/Home.module.scss
Normal file
130
03_source/mobile/src/pages/DemoReactNotes/pages/Home.module.scss
Normal file
@@ -0,0 +1,130 @@
|
||||
.heading {
|
||||
|
||||
color: var(--lighter-blue-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.mainTitle {
|
||||
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.slideHeader {
|
||||
|
||||
margin: 0 !important;
|
||||
padding: 0;
|
||||
padding: 1.5rem !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.slideCount {
|
||||
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
padding-left: 1.5rem !important;
|
||||
padding-top: 1rem !important;
|
||||
}
|
||||
|
||||
.slideCount h6 {
|
||||
|
||||
color: var(--light-blue-color);
|
||||
}
|
||||
|
||||
.slideHeader h4 {
|
||||
|
||||
color: white;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
text-align: left;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.categorySlider {
|
||||
|
||||
margin-top: -1.6rem;
|
||||
|
||||
ion-slide {
|
||||
|
||||
width: 60%;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
ion-col {
|
||||
|
||||
padding-left: 0;
|
||||
|
||||
ion-card {
|
||||
|
||||
width: 100%;
|
||||
border-radius: 22px;
|
||||
// box-shadow: 0px 4px 12px 2px rgba(0, 0, 0, 0.16);
|
||||
|
||||
box-shadow: 0 0.7px 0.9px rgba(0, 0, 0, 0.101),
|
||||
0 1.9px 2.5px rgba(0, 0, 0, 0.145),
|
||||
0 4.5px 6px rgba(0, 0, 0, 0.189),
|
||||
0 15px 20px rgba(0, 0, 0, 0.29);
|
||||
|
||||
ion-card-header {
|
||||
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
ion-card-content {
|
||||
|
||||
padding: 0;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lastUsed {
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: flex-start;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.9rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.categoryColor {
|
||||
|
||||
border-bottom: 3px solid var(--pink-color);
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.recentNotes {
|
||||
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
max-height: 18.8rem;
|
||||
overflow: scroll;
|
||||
|
||||
ion-item {
|
||||
|
||||
--padding-start: 2rem;
|
||||
--padding-end: 2rem;
|
||||
--border-style: none;
|
||||
--border-radius: 22px;
|
||||
--min-height: 4rem;
|
||||
|
||||
h4 {
|
||||
|
||||
padding-left: 1rem;
|
||||
margin-top: 0.6rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottomContainer {
|
||||
|
||||
margin-top: -1rem;
|
||||
}
|
166
03_source/mobile/src/pages/DemoReactNotes/pages/Home.tsx
Normal file
166
03_source/mobile/src/pages/DemoReactNotes/pages/Home.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
import {
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonCardHeader,
|
||||
IonCardSubtitle,
|
||||
IonCheckbox,
|
||||
IonCol,
|
||||
IonContent,
|
||||
IonFab,
|
||||
IonFabButton,
|
||||
IonGrid,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonItem,
|
||||
IonPage,
|
||||
IonRow,
|
||||
// IonSlide,
|
||||
// IonSlides,
|
||||
IonToolbar,
|
||||
} from '@ionic/react';
|
||||
import styles from './Home.module.scss';
|
||||
|
||||
import { addOutline, menuOutline, notificationsOutline, searchOutline } from 'ionicons/icons';
|
||||
import { getCategories, getNotes } from '../store/Selectors';
|
||||
import { CategoryStore, NoteStore } from '../store';
|
||||
import { markNote } from '../store/NoteStore';
|
||||
|
||||
const Home = (): React.JSX.Element => {
|
||||
return <>TODO: need update IonSlide</>;
|
||||
|
||||
const categories = CategoryStore.useState(getCategories);
|
||||
const notes = NoteStore.useState(getNotes);
|
||||
|
||||
const getNoteStyle = (categoryID, isComplete = false) => {
|
||||
const categoryColor = categories.filter((category) => category.id === categoryID)[0].color;
|
||||
|
||||
return {
|
||||
'--background': categoryColor,
|
||||
'--background-checked': categoryColor,
|
||||
'--border-style': 'none',
|
||||
opacity: isComplete ? '0.6' : '1',
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonButton>
|
||||
<IonIcon icon={menuOutline} />
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
|
||||
<IonButtons slot="end">
|
||||
<IonButton>
|
||||
<IonIcon icon={searchOutline} />
|
||||
</IonButton>
|
||||
|
||||
<IonButton>
|
||||
<IonIcon icon={notificationsOutline} />
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent fullscreen>
|
||||
<IonGrid>
|
||||
<IonRow>
|
||||
<IonCol size="12" className="ion-padding-start">
|
||||
<h1 className={styles.mainTitle}>Hello, Alan!</h1>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow>
|
||||
<IonCol size="12" className="ion-padding-start ion-padding-top">
|
||||
<IonCardSubtitle className={styles.heading}>Categories</IonCardSubtitle>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
|
||||
<IonSlides
|
||||
id="slider"
|
||||
options={{ slidesPerView: 'auto', zoom: true, grabCursor: true }}
|
||||
className={`${styles.categorySlider} ion-padding-bottom`}
|
||||
>
|
||||
{categories.map((category, index) => {
|
||||
const noteCount = notes.filter((n) => n.category_id === category.id).length;
|
||||
|
||||
return (
|
||||
<IonSlide key={`categorySlide_${index}`}>
|
||||
<IonCol className="ion-text-left">
|
||||
<IonCard>
|
||||
<IonCardHeader className="ion-no-padding">
|
||||
<div className={styles.slideCount}>
|
||||
<h6>
|
||||
{noteCount} {noteCount === 1 ? 'note' : 'notes'}{' '}
|
||||
</h6>
|
||||
</div>
|
||||
<div className={styles.slideHeader}>
|
||||
<h4>{category.name}</h4>
|
||||
</div>
|
||||
</IonCardHeader>
|
||||
|
||||
<IonCardContent>
|
||||
<div
|
||||
className={styles.categoryColor}
|
||||
style={{ borderBottom: `2px solid ${category.color}` }}
|
||||
></div>
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
</IonCol>
|
||||
</IonSlide>
|
||||
);
|
||||
})}
|
||||
</IonSlides>
|
||||
|
||||
<IonGrid className={styles.bottomContainer}>
|
||||
<IonRow>
|
||||
<IonCol size="12" className="ion-padding-start">
|
||||
<IonCardSubtitle className={styles.heading}>Recent Notes</IonCardSubtitle>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<div className={styles.recentNotes}>
|
||||
{notes.map((note, index) => {
|
||||
return (
|
||||
<IonRow
|
||||
key={`note_${index}`}
|
||||
className="animate__animated animate__faster"
|
||||
id={`noteRow_${note.id}`}
|
||||
>
|
||||
<IonCol size="12">
|
||||
<IonItem>
|
||||
<IonCheckbox
|
||||
checked={note.complete}
|
||||
style={getNoteStyle(note.category_id, note.complete)}
|
||||
onClick={() => markNote(note.id)}
|
||||
/>
|
||||
<h4
|
||||
style={
|
||||
note.complete ? { textDecoration: 'line-through', opacity: '0.6' } : {}
|
||||
}
|
||||
>
|
||||
{note.note}
|
||||
</h4>
|
||||
</IonItem>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</IonGrid>
|
||||
|
||||
<IonFab vertical="bottom" horizontal="end" slot="fixed" className="ion-padding">
|
||||
<IonFabButton routerLink="/add">
|
||||
<IonIcon icon={addOutline} />
|
||||
</IonFabButton>
|
||||
</IonFab>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
@@ -0,0 +1,33 @@
|
||||
import { Store } from 'pullstate';
|
||||
|
||||
interface Category {
|
||||
id: number;
|
||||
name: string;
|
||||
count: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
const CategoryStore = new Store({
|
||||
categories: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Business',
|
||||
count: '34',
|
||||
color: '#60b660',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Personal',
|
||||
count: '12',
|
||||
color: '#1D68DF',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Leisure',
|
||||
count: '23',
|
||||
color: '#EB06FF',
|
||||
},
|
||||
] as Category[],
|
||||
});
|
||||
|
||||
export default CategoryStore;
|
58
03_source/mobile/src/pages/DemoReactNotes/store/NoteStore.ts
Normal file
58
03_source/mobile/src/pages/DemoReactNotes/store/NoteStore.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Store } from 'pullstate';
|
||||
|
||||
interface Note {
|
||||
id: number;
|
||||
category_id: number;
|
||||
note: string;
|
||||
complete: boolean;
|
||||
}
|
||||
|
||||
const NoteStore = new Store({
|
||||
notes: [
|
||||
{
|
||||
id: 1,
|
||||
category_id: 1,
|
||||
note: 'Daily meeting with team',
|
||||
complete: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
category_id: 2,
|
||||
note: 'Pay monthly rent',
|
||||
complete: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
category_id: 3,
|
||||
note: 'Workout in the gym',
|
||||
complete: false,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category_id: 1,
|
||||
note: 'Make progress on project',
|
||||
complete: false,
|
||||
},
|
||||
] as Note[],
|
||||
});
|
||||
|
||||
export const markNote = (noteID: number) => {
|
||||
const noteIndex = NoteStore.currentState.notes.findIndex((n) => n.id === noteID);
|
||||
NoteStore.update((s) => {
|
||||
s.notes[noteIndex].complete = !s.notes[noteIndex].complete;
|
||||
});
|
||||
|
||||
document.getElementById(`noteRow_${noteID}`).classList.add('animate__pulse');
|
||||
|
||||
setTimeout(() => {
|
||||
document.getElementById(`noteRow_${noteID}`).classList.remove('animate__pulse');
|
||||
}, 500);
|
||||
};
|
||||
|
||||
export const addNote = (note: Note) => {
|
||||
NoteStore.update((s) => {
|
||||
s.notes = [note, ...s.notes];
|
||||
});
|
||||
};
|
||||
|
||||
export default NoteStore;
|
16
03_source/mobile/src/pages/DemoReactNotes/store/Selectors.ts
Normal file
16
03_source/mobile/src/pages/DemoReactNotes/store/Selectors.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { Category, Note } from './NoteStore';
|
||||
|
||||
interface State {
|
||||
categories: Category[];
|
||||
notes: Note[];
|
||||
}
|
||||
|
||||
const getState = (state: State) => state;
|
||||
|
||||
// General getters
|
||||
export const getCategories = createSelector(getState, (state) => state.categories);
|
||||
export const getNotes = createSelector(getState, (state) => state.notes);
|
||||
|
||||
// More specific getters
|
||||
// export const getCoffee = (id: number) => createSelector(getState, state => state.coffees.filter(c => parseInt(c.id) === parseInt(id))[0]);
|
2
03_source/mobile/src/pages/DemoReactNotes/store/index.js
Normal file
2
03_source/mobile/src/pages/DemoReactNotes/store/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as CategoryStore } from "./CategoryStore";
|
||||
export { default as NoteStore } from "./NoteStore";
|
@@ -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;
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
import { IonSlide, IonButton, IonGrid, IonRow, IonCol } from '@ionic/react';
|
||||
import '../pages/Home.css';
|
||||
|
||||
interface OnboardingSlideProps {
|
||||
image: string;
|
||||
mainSlide?: boolean;
|
||||
finalSlide?: boolean;
|
||||
title: string;
|
||||
text: string;
|
||||
lastSlide?: boolean;
|
||||
sliderRef: React.RefObject<any>;
|
||||
}
|
||||
|
||||
const OnboardingSlide = ({
|
||||
image,
|
||||
mainSlide = false,
|
||||
finalSlide = false,
|
||||
title,
|
||||
text,
|
||||
lastSlide,
|
||||
sliderRef,
|
||||
}: OnboardingSlideProps): React.JSX.Element => {
|
||||
return (
|
||||
<IonSlide>
|
||||
<IonGrid className="ion-justify-content-center ion-align-items-center ion-align-self-center">
|
||||
<IonRow className="slide-content-container">
|
||||
<IonCol size="10" className="slide-content">
|
||||
<img src={image} className={mainSlide && 'slide-main-image'} />
|
||||
<h1>{title}</h1>
|
||||
<p>{text}</p>
|
||||
|
||||
{mainSlide && (
|
||||
<IonButton
|
||||
expand="block"
|
||||
fill="outline"
|
||||
onClick={() => sliderRef.current.slideNext()}
|
||||
>
|
||||
Get started →
|
||||
</IonButton>
|
||||
)}
|
||||
|
||||
{finalSlide && (
|
||||
<>
|
||||
<IonButton expand="block" fill="solid">
|
||||
Register
|
||||
</IonButton>
|
||||
<IonButton expand="block" fill="outline">
|
||||
Login
|
||||
</IonButton>
|
||||
</>
|
||||
)}
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
</IonSlide>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnboardingSlide;
|
@@ -0,0 +1,52 @@
|
||||
ion-slides {
|
||||
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.slide-grid {
|
||||
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.slide-main-image {
|
||||
|
||||
height: 5rem !important;
|
||||
}
|
||||
|
||||
.slide-buttons {
|
||||
|
||||
position: absolute;
|
||||
bottom: 2rem;
|
||||
z-index: 10;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.slide-content-container {
|
||||
|
||||
margin-top: -4rem;
|
||||
}
|
||||
|
||||
.slide-content {
|
||||
|
||||
margin: 0 auto;
|
||||
/* margin-top: 5rem; */
|
||||
color: var(--ion-color-primary);
|
||||
/* background-color: var(--ion-color-primary); */
|
||||
/* color: white; */
|
||||
border: 2px solid rgb(228, 228, 228);
|
||||
border-radius: 15px;
|
||||
padding: 3rem;
|
||||
/* padding-left: 3rem; */
|
||||
/* padding-right: 3rem; */
|
||||
}
|
||||
|
||||
.slide-content p {
|
||||
|
||||
color: rgb(161, 161, 161);
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
import { IonButton, IonContent, IonIcon, IonPage, IonRow, IonSlides } from '@ionic/react';
|
||||
import { arrowBack, arrowForward } from 'ionicons/icons';
|
||||
import { useRef, useState } from 'react';
|
||||
import OnboardingSlide from '../components/OnboardingSlide';
|
||||
import './Home.css';
|
||||
|
||||
interface SlideContent {
|
||||
image: string;
|
||||
mainSlide?: boolean;
|
||||
finalSlide?: boolean;
|
||||
title: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const Home = (): React.JSX.Element => {
|
||||
const sliderRef = useRef<HTMLIonSlidesElement>(null);
|
||||
const [lastSlide, setLastSlide] = useState(false);
|
||||
const [firstSlide, setFirstSlide] = useState(true);
|
||||
|
||||
const slideContent: SlideContent[] = [
|
||||
{
|
||||
image: '/assets/applogo1.png',
|
||||
mainSlide: true,
|
||||
title: 'Ionic Onboarding UI',
|
||||
text: 'Share moments with your followers and experience memorable captures',
|
||||
},
|
||||
{
|
||||
image: '/assets/1sub.png',
|
||||
title: 'Capture',
|
||||
text: 'Capture that perfect moment in your life',
|
||||
},
|
||||
{
|
||||
image: '/assets/2sub.png',
|
||||
title: 'Organize',
|
||||
text: 'Organize photos exactly how you want them',
|
||||
},
|
||||
{
|
||||
image: '/assets/3sub.png',
|
||||
title: 'Share',
|
||||
finalSlide: true,
|
||||
text: 'Are you ready to share your special moments online with the world?',
|
||||
},
|
||||
];
|
||||
|
||||
const checkSlides = async () => {
|
||||
if (!sliderRef.current) return;
|
||||
|
||||
const isLastSlide = await sliderRef.current.isEnd();
|
||||
const isFirstSlide = await sliderRef.current.isBeginning();
|
||||
setLastSlide(isLastSlide);
|
||||
setFirstSlide(isFirstSlide);
|
||||
};
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<IonSlides
|
||||
onIonSlideWillChange={checkSlides}
|
||||
pager={true}
|
||||
ref={sliderRef}
|
||||
id="slider"
|
||||
options={{ slidesPerView: 'auto', zoom: true, grabCursor: true }}
|
||||
>
|
||||
{slideContent.map((slide, index) => {
|
||||
return (
|
||||
<OnboardingSlide key={index} {...slide} lastSlide={lastSlide} sliderRef={sliderRef} />
|
||||
);
|
||||
})}
|
||||
</IonSlides>
|
||||
|
||||
<IonRow className="slide-buttons">
|
||||
{!firstSlide && (
|
||||
<IonButton fill="clear" onClick={() => sliderRef.current?.slidePrev()}>
|
||||
<IonIcon icon={arrowBack} />
|
||||
</IonButton>
|
||||
)}
|
||||
|
||||
{!lastSlide && (
|
||||
<IonButton fill="clear" onClick={() => sliderRef.current?.slideNext()}>
|
||||
<IonIcon icon={arrowForward} />
|
||||
</IonButton>
|
||||
)}
|
||||
</IonRow>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
@@ -0,0 +1,82 @@
|
||||
/* 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;
|
||||
}
|
||||
|
||||
.swiper-pagination {
|
||||
|
||||
margin-bottom: 1rem;
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
ion-menu ion-content {
|
||||
--background: var(--ion-item-background, var(--ion-background-color, #fff));
|
||||
}
|
||||
|
||||
ion-menu.md ion-content {
|
||||
--padding-start: 8px;
|
||||
--padding-end: 8px;
|
||||
--padding-top: 20px;
|
||||
--padding-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
ion-menu.md ion-note {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list-header, ion-menu.md ion-note {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#inbox-list {
|
||||
border-bottom: 1px solid var(--ion-color-step-150, #d7d8da);
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#inbox-list ion-list-header {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#labels-list ion-list-header {
|
||||
font-size: 16px;
|
||||
margin-bottom: 18px;
|
||||
color: #757575;
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item {
|
||||
--padding-start: 10px;
|
||||
--padding-end: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item.selected {
|
||||
--background: rgba(var(--ion-color-primary-rgb), 0.14);
|
||||
}
|
||||
|
||||
ion-menu.md ion-item.selected ion-icon {
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
ion-menu.md ion-item ion-icon {
|
||||
color: #616e7e;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item ion-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-content {
|
||||
--padding-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list {
|
||||
padding: 20px 0 0 0;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-note {
|
||||
line-height: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item {
|
||||
--padding-start: 16px;
|
||||
--padding-end: 16px;
|
||||
--min-height: 50px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item ion-icon {
|
||||
font-size: 24px;
|
||||
color: #73849a;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item .selected ion-icon {
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list#labels-list ion-list-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list-header,
|
||||
ion-menu.ios ion-note {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-note {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
ion-note {
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
color: var(--ion-color-medium-shade);
|
||||
}
|
||||
|
||||
ion-item.selected {
|
||||
--color: var(--ion-color-primary);
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
import { IonContent, IonIcon, IonItem, IonLabel, IonList, IonListHeader, IonMenu, IonMenuToggle, IonNote } from '@ionic/react';
|
||||
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { star, starOutline } from 'ionicons/icons';
|
||||
import './Menu.css';
|
||||
|
||||
const Menu = ({ pages }) => {
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<IonMenu contentId="main" type="overlay">
|
||||
<IonContent>
|
||||
<IonList id="inbox-list">
|
||||
<IonListHeader>Overlay Hooks</IonListHeader>
|
||||
<IonNote>Choose one below to see a demo</IonNote>
|
||||
|
||||
{ pages.map((appPage, index) => {
|
||||
|
||||
const isSelected = location.pathname === appPage.url;
|
||||
|
||||
return (
|
||||
<IonMenuToggle key={ index } autoHide={false}>
|
||||
<IonItem className={ isSelected ? 'selected' : '' } routerLink={ appPage.url } routerDirection="none" lines="none" detail={false}>
|
||||
<IonIcon slot="start" icon={ isSelected ? star : starOutline } />
|
||||
<IonLabel>{ appPage.label }</IonLabel>
|
||||
</IonItem>
|
||||
</IonMenuToggle>
|
||||
);
|
||||
})}
|
||||
</IonList>
|
||||
</IonContent>
|
||||
</IonMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default Menu;
|
@@ -0,0 +1,58 @@
|
||||
import { IonButton, IonButtons, IonContent, IonHeader, IonMenuButton, IonPage, IonTitle, IonToolbar, useIonActionSheet } from '@ionic/react';
|
||||
|
||||
const ActionSheet = () => {
|
||||
|
||||
const [ present, dismiss ] = useIonActionSheet();
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Action Sheet</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Action Sheet</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [{ text: 'Ok' }, { text: 'Cancel' }],
|
||||
header: 'Action Sheet'
|
||||
})
|
||||
}
|
||||
>
|
||||
Show ActionSheet
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet')
|
||||
}
|
||||
>
|
||||
Show ActionSheet using params
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet');
|
||||
setTimeout(dismiss, 3000);
|
||||
}}
|
||||
>
|
||||
Show ActionSheet, hide after 3 seconds
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionSheet;
|
@@ -0,0 +1,53 @@
|
||||
import { IonButton, IonButtons, IonContent, IonHeader, IonMenuButton, IonPage, IonTitle, IonToolbar, useIonAlert } from '@ionic/react';
|
||||
|
||||
const Alert = () => {
|
||||
|
||||
const [ present ] = useIonAlert();
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Alert</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Alert</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
cssClass: 'my-css',
|
||||
header: 'Alert',
|
||||
message: 'alert from hook',
|
||||
buttons: [
|
||||
'Cancel',
|
||||
{ text: 'Ok', handler: (d) => console.log('ok pressed') },
|
||||
],
|
||||
onDidDismiss: (e) => console.log('did dismiss'),
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Alert
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => present('hello with params', [{ text: 'Ok' }])}
|
||||
>
|
||||
Show Alert using params
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Alert;
|
@@ -0,0 +1,54 @@
|
||||
import { IonButtons, IonCard, IonCardHeader, IonContent, IonHeader, IonMenuButton, IonPage, IonTitle, IonToolbar, IonCardTitle, IonCardSubtitle, IonCardContent, IonText } from '@ionic/react';
|
||||
|
||||
const All = () => {
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>All</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">All</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonCard>
|
||||
<IonCardHeader>
|
||||
<IonCardSubtitle>Sample usage</IonCardSubtitle>
|
||||
<IonCardTitle>Overlay Hooks</IonCardTitle>
|
||||
</IonCardHeader>
|
||||
|
||||
<IonCardContent>
|
||||
<IonText>
|
||||
<p>
|
||||
In Ionic React 5.6, the team packaged up a new set of hooks for controlling overlay components that they thought we might like. What is an overlay you ask? It’s the term that Ionic give components that display over your current content, such as alerts, modals, toasts, etc.
|
||||
</p>
|
||||
</IonText>
|
||||
<br />
|
||||
<IonText>
|
||||
<p>
|
||||
All of the code is taken from the Ionic Framework docs. You can find the blog post outlining these new overlay hooks <a href="https://ionicframework.com/blog/introducing-the-new-overlay-hooks-for-ionic-react/" target="_blank" rel="noreferrer">here.</a>
|
||||
</p>
|
||||
</IonText>
|
||||
<br />
|
||||
<IonText>
|
||||
<p>
|
||||
Check out the samples by navigating to a respective one in the side menu.
|
||||
</p>
|
||||
</IonText>
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default All;
|
@@ -0,0 +1,46 @@
|
||||
import { IonButton, IonButtons, IonContent, IonHeader, IonMenuButton, IonPage, IonTitle, IonToolbar, useIonLoading } from '@ionic/react';
|
||||
|
||||
const Loading = () => {
|
||||
|
||||
const [ present ] = useIonLoading();
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Loading</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Loading</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
duration: 3000,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Loading
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => present('Loading', 2000, 'dots')}
|
||||
>
|
||||
Show Loading using params
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Loading;
|
@@ -0,0 +1,68 @@
|
||||
import { IonButton, IonButtons, IonContent, IonHeader, IonMenuButton, IonPage, IonText, IonTitle, IonToolbar, useIonModal } from '@ionic/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
const Modal = () => {
|
||||
|
||||
const Body = ({ count, onDismiss, onIncrement }) => (
|
||||
<div className="ion-text-center">
|
||||
<IonText color="dark" className="ion-text-center">Count: { count }</IonText>
|
||||
<IonButton expand="block" onClick={ () => onIncrement() }>
|
||||
Increment Count
|
||||
</IonButton>
|
||||
<IonButton expand="block" onClick={ () => onDismiss() }>
|
||||
Close
|
||||
</IonButton>
|
||||
</div>
|
||||
);
|
||||
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const handleIncrement = () => {
|
||||
setCount(count + 1);
|
||||
};
|
||||
|
||||
const handleDismiss = () => {
|
||||
dismiss();
|
||||
};
|
||||
|
||||
const [present, dismiss] = useIonModal(Body, {
|
||||
count,
|
||||
onDismiss: handleDismiss,
|
||||
onIncrement: handleIncrement,
|
||||
});
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Modal</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Modal</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => {
|
||||
present({
|
||||
cssClass: 'my-class',
|
||||
});
|
||||
}}
|
||||
>
|
||||
Show Modal
|
||||
</IonButton>
|
||||
<div>Count: {count}</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Modal;
|
@@ -0,0 +1,97 @@
|
||||
import { IonButton, IonButtons, IonContent, IonHeader, IonMenuButton, IonPage, IonTitle, IonToolbar, useIonPicker } from '@ionic/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
const Picker = () => {
|
||||
|
||||
const [ present ] = useIonPicker();
|
||||
const [ value, setValue ] = useState('');
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Picker</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Picker</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
setValue(selected.animal.value)
|
||||
},
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Picker
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present(
|
||||
[
|
||||
{
|
||||
name: 'animal',
|
||||
options: [
|
||||
{ text: 'Dog', value: 'dog' },
|
||||
{ text: 'Cat', value: 'cat' },
|
||||
{ text: 'Bird', value: 'bird' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'vehicle',
|
||||
options: [
|
||||
{ text: 'Car', value: 'car' },
|
||||
{ text: 'Truck', value: 'truck' },
|
||||
{ text: 'Bike', value: 'bike' },
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
text: 'Confirm',
|
||||
handler: (selected) => {
|
||||
setValue(`${selected.animal.value}, ${selected.vehicle.value}`)
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
}
|
||||
>
|
||||
Show Picker using params
|
||||
</IonButton>
|
||||
{value && (
|
||||
<div>Selected Value: {value}</div>
|
||||
)}
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Picker;
|
@@ -0,0 +1,53 @@
|
||||
import { IonButtons, IonContent, IonHeader, IonItem, IonListHeader, IonMenuButton, IonPage, IonTitle, IonToolbar, IonList, useIonPopover, IonButton } from '@ionic/react';
|
||||
|
||||
const Popover = () => {
|
||||
|
||||
const PopoverList = ({ onHide }) => (
|
||||
<IonList>
|
||||
<IonListHeader>Ionic</IonListHeader>
|
||||
<IonItem button>Learn Ionic</IonItem>
|
||||
<IonItem button>Documentation</IonItem>
|
||||
<IonItem button>Showcase</IonItem>
|
||||
<IonItem button>GitHub Repo</IonItem>
|
||||
<IonItem lines="none" detail={false} button onClick={ onHide }>
|
||||
Close
|
||||
</IonItem>
|
||||
</IonList>
|
||||
);
|
||||
|
||||
const [ present, dismiss ] = useIonPopover(PopoverList, { onHide: () => dismiss() });
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Popover</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Popover</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={(e) =>
|
||||
present({
|
||||
event: e.nativeEvent,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Popover
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Popover;
|
@@ -0,0 +1,52 @@
|
||||
import { IonButton, IonButtons, IonContent, IonHeader, IonMenuButton, IonPage, IonTitle, IonToolbar, useIonToast } from '@ionic/react';
|
||||
|
||||
const Toast = () => {
|
||||
|
||||
const [ present, dismiss ] = useIonToast();
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Toast</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Toast</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() =>
|
||||
present({
|
||||
buttons: [{ text: 'hide', handler: () => dismiss() }],
|
||||
message: 'toast from hook, click hide to dismiss',
|
||||
onDidDismiss: () => console.log('dismissed'),
|
||||
onWillDismiss: () => console.log('will dismiss'),
|
||||
})
|
||||
}
|
||||
>
|
||||
Show Toast
|
||||
</IonButton>
|
||||
<IonButton
|
||||
expand="block"
|
||||
onClick={() => present('hello from hook', 3000)}
|
||||
>
|
||||
Show Toast using params, closes in 3 secs
|
||||
</IonButton>
|
||||
<IonButton expand="block" onClick={dismiss}>
|
||||
Hide Toast
|
||||
</IonButton>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Toast;
|
@@ -0,0 +1,77 @@
|
||||
/* 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;
|
||||
}
|
113
03_source/mobile/src/pages/DemoReactQuotes/components/Menu.css
Normal file
113
03_source/mobile/src/pages/DemoReactQuotes/components/Menu.css
Normal file
@@ -0,0 +1,113 @@
|
||||
ion-menu ion-content {
|
||||
--background: var(--ion-item-background, var(--ion-background-color, #fff));
|
||||
}
|
||||
|
||||
ion-menu.md ion-content {
|
||||
--padding-start: 8px;
|
||||
--padding-end: 8px;
|
||||
--padding-top: 20px;
|
||||
--padding-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
ion-menu.md ion-note {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list-header, ion-menu.md ion-note {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#inbox-list {
|
||||
border-bottom: 1px solid var(--ion-color-step-150, #d7d8da);
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#inbox-list ion-list-header {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-list#labels-list ion-list-header {
|
||||
font-size: 16px;
|
||||
margin-bottom: 18px;
|
||||
color: #757575;
|
||||
min-height: 26px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item {
|
||||
--padding-start: 10px;
|
||||
--padding-end: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item.selected {
|
||||
--background: rgba(var(--ion-color-primary-rgb), 0.14);
|
||||
}
|
||||
|
||||
ion-menu.md ion-item.selected ion-icon {
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
ion-menu.md ion-item ion-icon {
|
||||
color: #616e7e;
|
||||
}
|
||||
|
||||
ion-menu.md ion-item ion-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-content {
|
||||
--padding-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list {
|
||||
padding: 20px 0 0 0;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-note {
|
||||
line-height: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item {
|
||||
--padding-start: 16px;
|
||||
--padding-end: 16px;
|
||||
--min-height: 50px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item ion-icon {
|
||||
font-size: 24px;
|
||||
color: #73849a;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-item .selected ion-icon {
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list#labels-list ion-list-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-list-header,
|
||||
ion-menu.ios ion-note {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
ion-menu.ios ion-note {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
ion-note {
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
color: var(--ion-color-medium-shade);
|
||||
}
|
||||
|
||||
ion-item.selected {
|
||||
--color: var(--ion-color-primary);
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
IonContent,
|
||||
IonIcon,
|
||||
IonItem,
|
||||
IonLabel,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonMenu,
|
||||
IonMenuToggle,
|
||||
IonNote,
|
||||
} from '@ionic/react';
|
||||
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { bookmarkOutline, bookmarkSharp, homeOutline, homeSharp } from 'ionicons/icons';
|
||||
import './Menu.css';
|
||||
import { useStoreState } from 'pullstate';
|
||||
import { QuoteStore } from '../store';
|
||||
import { getSavedQuotes } from '../store/Selectors';
|
||||
|
||||
const Menu = () => {
|
||||
|
||||
const location = useLocation();
|
||||
const saved = useStoreState(QuoteStore, getSavedQuotes);
|
||||
|
||||
const appPages = [
|
||||
{
|
||||
title: 'Home',
|
||||
url: '/home',
|
||||
iosIcon: homeOutline,
|
||||
mdIcon: homeSharp
|
||||
},
|
||||
{
|
||||
title: `Bookmarks (${ saved.length })`,
|
||||
url: '/saved',
|
||||
iosIcon: bookmarkOutline,
|
||||
mdIcon: bookmarkSharp
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<IonMenu contentId="main" type="overlay">
|
||||
<IonContent>
|
||||
<IonList id="inbox-list" className="ion-margin-top">
|
||||
<IonListHeader>Ionic Quotes</IonListHeader>
|
||||
<IonNote>hey there!</IonNote>
|
||||
{appPages.map((appPage, index) => {
|
||||
return (
|
||||
<IonMenuToggle key={index} autoHide={false}>
|
||||
<IonItem className={location.pathname === appPage.url ? 'selected' : ''} routerLink={appPage.url} routerDirection="none" lines="none" detail={false}>
|
||||
<IonIcon slot="start" ios={appPage.iosIcon} md={appPage.mdIcon} />
|
||||
<IonLabel>{appPage.title}</IonLabel>
|
||||
</IonItem>
|
||||
</IonMenuToggle>
|
||||
);
|
||||
})}
|
||||
</IonList>
|
||||
</IonContent>
|
||||
</IonMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export default Menu;
|
@@ -0,0 +1,21 @@
|
||||
.quoteItem {
|
||||
|
||||
--quote-item-background: rgb(49, 117, 226);
|
||||
|
||||
border: 2px solid rgb(154, 204, 245);
|
||||
border-radius: 10px;
|
||||
--background: var(--quote-item-background);
|
||||
background: var(--quote-item-background);
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.quoteText p {
|
||||
|
||||
color: rgb(25, 51, 93);
|
||||
}
|
||||
|
||||
.quoteText h1:hover {
|
||||
|
||||
color: white;
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import { IonCol, IonItem, IonLabel } from "@ionic/react";
|
||||
import styles from "./QuoteItem.module.css";
|
||||
|
||||
export const QuoteItem = ({ quote }) => {
|
||||
|
||||
return (
|
||||
|
||||
<IonCol size="6" className="animate__animated animate__fadeIn">
|
||||
<IonItem lines="none" className={ styles.quoteItem } routerLink={ `/quote/${ quote.id }`}>
|
||||
<IonLabel className={ styles.quoteText }>
|
||||
<h2>{ quote.text }</h2>
|
||||
<p>{ quote.author }</p>
|
||||
</IonLabel>
|
||||
</IonItem>
|
||||
</IonCol>
|
||||
);
|
||||
}
|
62
03_source/mobile/src/pages/DemoReactQuotes/pages/Home.tsx
Normal file
62
03_source/mobile/src/pages/DemoReactQuotes/pages/Home.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { IonButtons, IonContent, IonGrid, IonHeader, IonInfiniteScroll, IonInfiniteScrollContent, IonList, IonMenuButton, IonPage, IonRow, IonTitle, IonToolbar } from '@ionic/react';
|
||||
import { useStoreState } from 'pullstate';
|
||||
import { useState } from 'react';
|
||||
import { QuoteItem } from '../components/QuoteItem';
|
||||
import { QuoteStore } from '../store';
|
||||
import { getQuotes } from '../store/Selectors';
|
||||
|
||||
const Home = () => {
|
||||
|
||||
const quotes = useStoreState(QuoteStore, getQuotes);
|
||||
const [ amountLoaded, setAmountLoaded ] = useState(20);
|
||||
|
||||
const fetchMore = async e => {
|
||||
|
||||
setAmountLoaded(amountLoaded => amountLoaded + 20);
|
||||
e.target.complete();
|
||||
}
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonMenuButton />
|
||||
</IonButtons>
|
||||
<IonTitle>Home</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Home</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonGrid>
|
||||
<IonList>
|
||||
<IonRow>
|
||||
{ quotes.map((quote, index) => {
|
||||
|
||||
if ((index <= amountLoaded) && quote.author) {
|
||||
return (
|
||||
|
||||
<QuoteItem key={ index } quote={ quote } />
|
||||
);
|
||||
} else return "";
|
||||
})}
|
||||
</IonRow>
|
||||
</IonList>
|
||||
|
||||
<IonInfiniteScroll threshold="200px" onIonInfinite={ fetchMore }>
|
||||
<IonInfiniteScrollContent loadingSpinner="bubbles" loadingText="Getting more quotes...">
|
||||
</IonInfiniteScrollContent>
|
||||
</IonInfiniteScroll>
|
||||
</IonGrid>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
88
03_source/mobile/src/pages/DemoReactQuotes/pages/Quote.tsx
Normal file
88
03_source/mobile/src/pages/DemoReactQuotes/pages/Quote.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { IonBackButton, IonButton, IonButtons, IonCard, IonCardContent, IonCol, IonContent, IonHeader, IonIcon, IonImg, IonPage, IonRow, IonTitle, IonToolbar, useIonToast } from '@ionic/react';
|
||||
import { bookmarkOutline, checkmarkOutline, copyOutline } from 'ionicons/icons';
|
||||
import { useStoreState } from 'pullstate';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import { QuoteStore } from '../store';
|
||||
import { addSavedQuote, removeSavedQuote } from '../store/QuoteStore';
|
||||
import { getQuote, getSavedQuotes } from '../store/Selectors';
|
||||
|
||||
import { Clipboard } from '@capacitor/clipboard';
|
||||
|
||||
const Quote = () => {
|
||||
|
||||
const { id } = useParams();
|
||||
const quote = useStoreState(QuoteStore, getQuote(id));
|
||||
const saved = useStoreState(QuoteStore, getSavedQuotes);
|
||||
const [ bookmarked, setBookmarked ] = useState(false);
|
||||
|
||||
const [ present ] = useIonToast();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
setBookmarked(saved.includes(parseInt(id)));
|
||||
}, [ saved, id ]);
|
||||
|
||||
const copyQuote = async () => {
|
||||
|
||||
await Clipboard.write({
|
||||
|
||||
string: quote.text
|
||||
});
|
||||
|
||||
present({
|
||||
|
||||
header: "Success",
|
||||
message: "Quote copied to clipboard!",
|
||||
duration: 2500,
|
||||
color: "primary"
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
<IonHeader>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="start">
|
||||
<IonBackButton text="Home" />
|
||||
</IonButtons>
|
||||
<IonTitle>Quote</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonContent fullscreen>
|
||||
<IonHeader collapse="condense">
|
||||
<IonToolbar>
|
||||
<IonTitle size="large">Quote</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
|
||||
<IonCard className="animate__animated animate__slideInRight animate__faster">
|
||||
<IonImg src={ quote.image } alt="quote cover" />
|
||||
<IonCardContent>
|
||||
<h1>{ quote.text }</h1>
|
||||
<p>- { quote.author }</p>
|
||||
</IonCardContent>
|
||||
|
||||
<IonRow>
|
||||
<IonCol size="6">
|
||||
<IonButton fill={ !bookmarked ? "outline" : "solid" } onClick={ () => bookmarked ? removeSavedQuote(quote.id) : addSavedQuote(quote.id) }>
|
||||
<IonIcon icon={ bookmarked ? checkmarkOutline : bookmarkOutline } />
|
||||
{ bookmarked ? "Bookmarked" : "Save as Bookmark" }
|
||||
</IonButton>
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="4">
|
||||
<IonButton fill="outline" onClick={ copyQuote }>
|
||||
<IonIcon icon={ copyOutline } />
|
||||
Copy Quote
|
||||
</IonButton>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCard>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
);
|
||||
};
|
||||
|
||||
export default Quote;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user