update demo-react-notes,
This commit is contained in:
@@ -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 { Route, Redirect } from 'react-router';
|
||||||
|
|
||||||
import Tab1 from './AppPages/Tab1';
|
// import Tab1 from './AppPages/Tab1';
|
||||||
import Tab2 from './AppPages/Tab2';
|
// import Tab2 from './AppPages/Tab2';
|
||||||
|
|
||||||
import './style.scss';
|
import './theme/variables.scss';
|
||||||
|
import Home from './pages/Home';
|
||||||
|
import Add from './pages/Add';
|
||||||
|
|
||||||
function DemoReactNotes() {
|
function DemoReactNotes() {
|
||||||
return (
|
return (
|
||||||
<IonTabs>
|
<IonTabs className="demo-react-notes">
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path="/demo-react-notes/tab1">
|
<Route exact path="/demo-react-notes/home">
|
||||||
<Tab1 />
|
<Home />
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path="/demo-react-notes/tab2">
|
<Route exact path="/demo-react-notes/add">
|
||||||
<Tab2 />
|
<Add />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Redirect exact path="/demo-react-notes" to="/demo-react-notes/tab1" />
|
<Redirect exact path="/demo-react-notes" to="/demo-react-notes/home" />
|
||||||
</IonRouterOutlet>
|
</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>
|
</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;
|
|
||||||
}
|
|
Reference in New Issue
Block a user