Compare commits
16 Commits
develop/mo
...
84b223ff60
Author | SHA1 | Date | |
---|---|---|---|
![]() |
84b223ff60 | ||
![]() |
fca048074e | ||
![]() |
bc35e25616 | ||
![]() |
15f8d2e6aa | ||
![]() |
592a099f7b | ||
![]() |
4c1b30e5c6 | ||
![]() |
c765bb49a4 | ||
![]() |
9aeb58379d | ||
![]() |
6419567005 | ||
![]() |
e2076fe67b | ||
![]() |
766720e075 | ||
![]() |
19af60c410 | ||
![]() |
ed95621b2f | ||
![]() |
2258fd8fb9 | ||
![]() |
0f674badd9 | ||
![]() |
bc731ea2b8 |
@@ -77,12 +77,12 @@ const Chat = () => {
|
||||
const [toastMessage, setToastMessage] = useState("");
|
||||
|
||||
// Refs
|
||||
const contentRef = useRef();
|
||||
const contentRef = useRef(null);
|
||||
const swiperRefs = useRef([]);
|
||||
const textareaRef = useRef();
|
||||
const sideRef = useRef();
|
||||
const sendRef = useRef();
|
||||
const replyToAnimationRef = useRef();
|
||||
const textareaRef = useRef(null);
|
||||
const sideRef = useRef(null);
|
||||
const sendRef = useRef(null);
|
||||
const replyToAnimationRef = useRef(null);
|
||||
|
||||
const actionSheetButtons = [
|
||||
{
|
||||
@@ -104,7 +104,7 @@ const Chat = () => {
|
||||
icon: alertOutline,
|
||||
handler: () =>
|
||||
toaster(
|
||||
"I haven't implemented unsend :) Simple store update though",
|
||||
"I haven't implemented unsend :) Simple store update though"
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -165,11 +165,11 @@ const Chat = () => {
|
||||
const chatMessageID = elementID.includes("chatText")
|
||||
? parseInt(elementID.replace("chatText_", ""))
|
||||
: elementID.includes("chatTime")
|
||||
? parseInt(elementID.replace("chatTime_", ""))
|
||||
: parseInt(elementID.replace("chatBubble_", ""));
|
||||
? parseInt(elementID.replace("chatTime_", ""))
|
||||
: parseInt(elementID.replace("chatBubble_", ""));
|
||||
|
||||
const chatMessage = chat.filter(
|
||||
(message) => parseInt(message.id) === parseInt(chatMessageID),
|
||||
(message) => parseInt(message.id) === parseInt(chatMessageID)
|
||||
)[0];
|
||||
|
||||
setActionMessage(chatMessage);
|
||||
@@ -278,7 +278,7 @@ const Chat = () => {
|
||||
replyToMessage,
|
||||
replyToMessage ? replyToMessage.id : false,
|
||||
image,
|
||||
imagePath,
|
||||
imagePath
|
||||
);
|
||||
setMessage("");
|
||||
|
||||
@@ -329,7 +329,7 @@ const Chat = () => {
|
||||
fill="clear"
|
||||
onClick={() =>
|
||||
toaster(
|
||||
"As this is a UI only, video calling wouldn't work here.",
|
||||
"As this is a UI only, video calling wouldn't work here."
|
||||
)
|
||||
}
|
||||
>
|
||||
@@ -352,7 +352,7 @@ const Chat = () => {
|
||||
{chat.map((message, index) => {
|
||||
const repliedMessage = chat.filter(
|
||||
(subMessage) =>
|
||||
parseInt(subMessage.id) === parseInt(message.replyID),
|
||||
parseInt(subMessage.id) === parseInt(message.replyID)
|
||||
)[0];
|
||||
|
||||
return (
|
||||
@@ -360,7 +360,9 @@ const Chat = () => {
|
||||
ref={(ref) => (swiperRefs.current[index] = ref)}
|
||||
id={`chatBubble_${message.id}`}
|
||||
key={index}
|
||||
className={`chat-bubble ${message.sent ? "bubble-sent" : "bubble-received"}`}
|
||||
className={`chat-bubble ${
|
||||
message.sent ? "bubble-sent" : "bubble-received"
|
||||
}`}
|
||||
{...longPressEvent}
|
||||
>
|
||||
<div id={`chatText_${message.id}`}>
|
||||
|
@@ -22,7 +22,7 @@ import { useRef } from "react";
|
||||
import ContactModal from "../components/ContactModal";
|
||||
|
||||
const Chats = () => {
|
||||
const pageRef = useRef();
|
||||
const pageRef = useRef(null);
|
||||
const contacts = ContactStore.useState(getContacts);
|
||||
const latestChats = ChatStore.useState(getChats);
|
||||
|
||||
@@ -43,7 +43,7 @@ const Chats = () => {
|
||||
contacts
|
||||
.filter((c) => c.id === chat.contact_id)[0]
|
||||
.name.toLowerCase()
|
||||
.includes(searchTermLower),
|
||||
.includes(searchTermLower)
|
||||
);
|
||||
setResults(newResults);
|
||||
} else {
|
||||
|
@@ -3,5 +3,28 @@
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100
|
||||
}
|
||||
"printWidth": 100,
|
||||
"overrides": [
|
||||
{
|
||||
"files": "src/App.tsx",
|
||||
"options": {
|
||||
"printWidth": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "src/routes/*",
|
||||
"options": {
|
||||
"printWidth": 160
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.html",
|
||||
"legacy/**/*.js"
|
||||
],
|
||||
"options": {
|
||||
"tabWidth": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@
|
||||
"@hookform/resolvers": "^4.1.3",
|
||||
"@ionic/react": "^8.5.0",
|
||||
"@ionic/react-router": "^8.5.0",
|
||||
"@ionic/storage": "^4.0.0",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@react-hook/window-size": "^3.1.1",
|
||||
"@types/leaflet": "^1.9.17",
|
||||
@@ -29,10 +30,12 @@
|
||||
"pigeon-maps": "^0.22.1",
|
||||
"pullstate": "^1",
|
||||
"react": "19.0.0",
|
||||
"react-canvas-draw": "^1.2.1",
|
||||
"react-color": "^2.19.3",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "^7.55.0",
|
||||
"react-iconly": "^2.2.10",
|
||||
"react-leaflet": "^5.0.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-qr-code": "^2.0.15",
|
||||
|
BIN
03_source/mobile/public/assets/DemoReactAddToCart/camera.jpeg
Normal file
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 930 B |
BIN
03_source/mobile/public/assets/DemoReactAddToCart/icon/icon.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
03_source/mobile/public/assets/DemoReactAddToCart/macbook.jpeg
Normal file
After Width: | Height: | Size: 33 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
BIN
03_source/mobile/public/assets/DemoReactAddToCart/tv.jpeg
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
03_source/mobile/public/assets/DemoReactLogin/icon/favicon.png
Normal file
After Width: | Height: | Size: 930 B |
BIN
03_source/mobile/public/assets/DemoReactLogin/icon/icon.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
03_source/mobile/public/assets/DemoReactLogin/login2.jpeg
Normal file
After Width: | Height: | Size: 296 KiB |
1
03_source/mobile/public/assets/DemoReactLogin/shapes.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 930 B |
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "blue",
|
||||
"tab_bar_background_color": "blue",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "",
|
||||
"light_color": "",
|
||||
|
||||
"main_color_tint": "",
|
||||
"main_color_shade": "",
|
||||
|
||||
"light_color_tint": "",
|
||||
"light_color_shade": ""
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#5f361e",
|
||||
"tab_bar_background_color": "#5f361e",
|
||||
"toolbar_color": "#d1bfb5",
|
||||
"tab_bar_color": "#886551",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "#5f361e",
|
||||
"light_color": "#886551",
|
||||
|
||||
"main_color_tint": "#855a41",
|
||||
"main_color_shade": "#57331e",
|
||||
|
||||
"light_color_tint": "#9c7d6b",
|
||||
"light_color_shade": "#61412f"
|
||||
}
|
After Width: | Height: | Size: 474 KiB |
After Width: | Height: | Size: 359 KiB |
After Width: | Height: | Size: 240 KiB |
After Width: | Height: | Size: 425 KiB |
After Width: | Height: | Size: 279 KiB |
After Width: | Height: | Size: 360 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#c4b0c4",
|
||||
"tab_bar_background_color": "#c4b0c4",
|
||||
"toolbar_color": "#5c4153",
|
||||
"tab_bar_color": "#917788",
|
||||
"tab_bar_activated_color": "#5c4153",
|
||||
|
||||
"main_color": "#5c4153",
|
||||
"light_color": "#917788",
|
||||
|
||||
"main_color_tint": "#6b5463",
|
||||
"main_color_shade": "#4d3545",
|
||||
|
||||
"light_color_tint": "#a893a1",
|
||||
"light_color_shade": "#74606d"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#c93608",
|
||||
"tab_bar_background_color": "#c93608",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "#852506",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "#c93608",
|
||||
"light_color": "#c95834",
|
||||
|
||||
"main_color_tint": "#aa614a",
|
||||
"main_color_shade": "#692611",
|
||||
|
||||
"light_color_tint": "#d67151",
|
||||
"light_color_shade": "#a85439"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "green",
|
||||
"tab_bar_background_color": "green",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "",
|
||||
"light_color": "",
|
||||
|
||||
"main_color_tint": "",
|
||||
"main_color_shade": "",
|
||||
|
||||
"light_color_tint": "",
|
||||
"light_color_shade": ""
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#336b36",
|
||||
"tab_bar_background_color": "#336b36",
|
||||
"toolbar_color": "white",
|
||||
"tab_bar_color": "#1c421d",
|
||||
"tab_bar_activated_color": "white",
|
||||
|
||||
"main_color": "#336b36",
|
||||
"light_color": "#517953",
|
||||
|
||||
"main_color_tint": "#497a4b",
|
||||
"main_color_shade": "#305832",
|
||||
|
||||
"light_color_tint": "#738574",
|
||||
"light_color_shade": "#485849"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#7baec7",
|
||||
"tab_bar_background_color": "#7baec7",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "#49758b",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "#7baec7",
|
||||
"light_color": "#90bbcf",
|
||||
|
||||
"main_color_tint": "#96c0d5",
|
||||
"main_color_shade": "#6b93a7",
|
||||
|
||||
"light_color_tint": "#c3e2f1",
|
||||
"light_color_shade": "#89aec0"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#fd8f38",
|
||||
"tab_bar_background_color": "#fd8f38",
|
||||
"toolbar_color": "#815208",
|
||||
"tab_bar_color": "#ffb67c",
|
||||
"tab_bar_activated_color": "#815208",
|
||||
|
||||
"main_color": "#e49200",
|
||||
"light_color": "#ffb67c",
|
||||
|
||||
"main_color_tint": "#e6ae4b",
|
||||
"main_color_shade": "#b4780c",
|
||||
|
||||
"light_color_tint": "#fad4b6",
|
||||
"light_color_shade": "#ff9c4e"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "red",
|
||||
"tab_bar_background_color": "red",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "",
|
||||
"light_color": "",
|
||||
|
||||
"main_color_tint": "",
|
||||
"main_color_shade": "",
|
||||
|
||||
"light_color_tint": "",
|
||||
"light_color_shade": ""
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "blue",
|
||||
"tab_bar_background_color": "blue",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "",
|
||||
"light_color": "",
|
||||
|
||||
"main_color_tint": "",
|
||||
"main_color_shade": "",
|
||||
|
||||
"light_color_tint": "",
|
||||
"light_color_shade": ""
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#5f361e",
|
||||
"tab_bar_background_color": "#5f361e",
|
||||
"toolbar_color": "#d1bfb5",
|
||||
"tab_bar_color": "#886551",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "#5f361e",
|
||||
"light_color": "#886551",
|
||||
|
||||
"main_color_tint": "#855a41",
|
||||
"main_color_shade": "#57331e",
|
||||
|
||||
"light_color_tint": "#9c7d6b",
|
||||
"light_color_shade": "#61412f"
|
||||
}
|
After Width: | Height: | Size: 474 KiB |
After Width: | Height: | Size: 359 KiB |
After Width: | Height: | Size: 240 KiB |
After Width: | Height: | Size: 425 KiB |
After Width: | Height: | Size: 279 KiB |
After Width: | Height: | Size: 360 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#c4b0c4",
|
||||
"tab_bar_background_color": "#c4b0c4",
|
||||
"toolbar_color": "#5c4153",
|
||||
"tab_bar_color": "#917788",
|
||||
"tab_bar_activated_color": "#5c4153",
|
||||
|
||||
"main_color": "#5c4153",
|
||||
"light_color": "#917788",
|
||||
|
||||
"main_color_tint": "#6b5463",
|
||||
"main_color_shade": "#4d3545",
|
||||
|
||||
"light_color_tint": "#a893a1",
|
||||
"light_color_shade": "#74606d"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#c93608",
|
||||
"tab_bar_background_color": "#c93608",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "#852506",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "#c93608",
|
||||
"light_color": "#c95834",
|
||||
|
||||
"main_color_tint": "#aa614a",
|
||||
"main_color_shade": "#692611",
|
||||
|
||||
"light_color_tint": "#d67151",
|
||||
"light_color_shade": "#a85439"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "green",
|
||||
"tab_bar_background_color": "green",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "",
|
||||
"light_color": "",
|
||||
|
||||
"main_color_tint": "",
|
||||
"main_color_shade": "",
|
||||
|
||||
"light_color_tint": "",
|
||||
"light_color_shade": ""
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#336b36",
|
||||
"tab_bar_background_color": "#336b36",
|
||||
"toolbar_color": "white",
|
||||
"tab_bar_color": "#1c421d",
|
||||
"tab_bar_activated_color": "white",
|
||||
|
||||
"main_color": "#336b36",
|
||||
"light_color": "#517953",
|
||||
|
||||
"main_color_tint": "#497a4b",
|
||||
"main_color_shade": "#305832",
|
||||
|
||||
"light_color_tint": "#738574",
|
||||
"light_color_shade": "#485849"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#7baec7",
|
||||
"tab_bar_background_color": "#7baec7",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "#49758b",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "#7baec7",
|
||||
"light_color": "#90bbcf",
|
||||
|
||||
"main_color_tint": "#96c0d5",
|
||||
"main_color_shade": "#6b93a7",
|
||||
|
||||
"light_color_tint": "#c3e2f1",
|
||||
"light_color_shade": "#89aec0"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "#fd8f38",
|
||||
"tab_bar_background_color": "#fd8f38",
|
||||
"toolbar_color": "#815208",
|
||||
"tab_bar_color": "#ffb67c",
|
||||
"tab_bar_activated_color": "#815208",
|
||||
|
||||
"main_color": "#e49200",
|
||||
"light_color": "#ffb67c",
|
||||
|
||||
"main_color_tint": "#e6ae4b",
|
||||
"main_color_shade": "#b4780c",
|
||||
|
||||
"light_color_tint": "#fad4b6",
|
||||
"light_color_shade": "#ff9c4e"
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"toolbar_background_color": "red",
|
||||
"tab_bar_background_color": "red",
|
||||
"toolbar_color": "",
|
||||
"tab_bar_color": "",
|
||||
"tab_bar_activated_color": "",
|
||||
|
||||
"main_color": "",
|
||||
"light_color": "",
|
||||
|
||||
"main_color_tint": "",
|
||||
"main_color_shade": "",
|
||||
|
||||
"light_color_tint": "",
|
||||
"light_color_shade": ""
|
||||
}
|
BIN
03_source/mobile/public/assets/DemoSkeletonText/icon/favicon.png
Normal file
After Width: | Height: | Size: 930 B |
BIN
03_source/mobile/public/assets/DemoSkeletonText/icon/icon.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/1.png
Normal file
After Width: | Height: | Size: 772 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/10.png
Normal file
After Width: | Height: | Size: 298 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/2.png
Normal file
After Width: | Height: | Size: 795 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/3.png
Normal file
After Width: | Height: | Size: 822 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/4.png
Normal file
After Width: | Height: | Size: 736 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/5.png
Normal file
After Width: | Height: | Size: 590 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/6.png
Normal file
After Width: | Height: | Size: 611 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/7.png
Normal file
After Width: | Height: | Size: 750 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/8.png
Normal file
After Width: | Height: | Size: 236 KiB |
BIN
03_source/mobile/public/assets/DemoSkeletonText/scenery/9.png
Normal file
After Width: | Height: | Size: 465 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
@@ -54,49 +54,34 @@ import Tutorial from './pages/Tutorial';
|
||||
import HomeOrTutorial from './components/HomeOrTutorial';
|
||||
import { Schedule } from './models/Schedule';
|
||||
import RedirectToLogin from './components/RedirectToLogin';
|
||||
import EventDetail from './pages/EventDetail';
|
||||
import EventList from './pages/EventList';
|
||||
import MemberProfile from './pages/MemberProfile';
|
||||
import Settings from './pages/Settings';
|
||||
import NotImplemented from './pages/NotImplemented';
|
||||
import ChangeLanguage from './pages/ChangeLanguage';
|
||||
import ServiceAgreement from './pages/ServiceAgreement';
|
||||
import paths from './paths';
|
||||
import PrivacyAgreement from './pages/PrivacyAgreement';
|
||||
import AppRoute from './AppRoute';
|
||||
//
|
||||
// TODO: resume DemoReactShop
|
||||
// import DemoReactShop from './pages/DemoReactShop';
|
||||
import DemoWeatherApp from './pages/DemoWeatherApp';
|
||||
import DemoClubHouse from './pages/DemoClubHouse';
|
||||
import DemoScoreBoard from './pages/DemoScoreBoard';
|
||||
import DemoQuoteApp from './pages/DemoQuoteApp';
|
||||
import DemoQrScanner from './pages/DemoQrScanner';
|
||||
|
||||
// TODO: remove obsoleted DemoWeatherApp
|
||||
// import DemoWeatherApp from './pages/DemoWeatherApp';
|
||||
|
||||
// DemoShopAppUi
|
||||
import DemoShopAppUi from './pages/DemoShopAppUi';
|
||||
// DemoDictionaryApp
|
||||
import DemoDictionaryApp from './pages/DemoDictionaryApp';
|
||||
// demo-recipe-app
|
||||
import DemoRecipeApp from './pages/DemoRecipeApp';
|
||||
|
||||
// DemoSlidingProfile
|
||||
import DemoSlidingProfile from './pages/DemoSlidingProfile';
|
||||
|
||||
// DemoQuizApp
|
||||
import DemoQuizApp from './pages/DemoQuizApp';
|
||||
import DemoBlogPostUi from './pages/DemoBlogPostUi';
|
||||
import DemoReactTravelApp from './pages/DemoReactTravelApp';
|
||||
import DemoPinterestFloatingTabBar from './pages/DemoPinterestFloatingTabBar';
|
||||
import DemoRestaurantFinder from './pages/DemoRestaurantFinder';
|
||||
import DemoReactOverlayHooks from './pages/DemoReactOverlayHooks';
|
||||
import DemoReactSwitchTabs from './pages/DemoReactSwitchTabs';
|
||||
import DemoReactPollApp from './pages/DemoReactPollApp';
|
||||
import DemoReactWhatsAppClone from './pages/DemoReactWhatsAppClone';
|
||||
import Demo2FaExample from './pages/Demo2FaExample';
|
||||
import DemoAccordionTutorial from './pages/DemoAccordionTutorial';
|
||||
import DemoBankingUi from './pages/DemoBankingUi';
|
||||
import DemoCapacitorGoogleMapsTutorial from './pages/DemoCapacitorGoogleMapsTutorial';
|
||||
import DemoColorTutorial from './pages/DemoColorTutorial';
|
||||
|
||||
//
|
||||
// import DemoReactMarvelApp from './pages/DemoReactMarvelApp';
|
||||
// import DemoReactOnboardingUI from './pages/DemoReactOnboardingUI';
|
||||
// import DemoReactTabsMenusCustom from './pages/DemoReactTabsMenusCustom';
|
||||
// import DemoReactThemeSwitcher from './pages/DemoReactThemeSwitcher';
|
||||
// import DemoSkeletonText from './pages/DemoSkeletonText';
|
||||
// import DemoStickyBottomSheetExample from './pages/DemoStickyBottomSheetExample';
|
||||
// import DemoStorageExample from './pages/DemoStorageExample';
|
||||
// import DemoSwiperjsTutorial from './pages/DemoSwiperjsTutorial';
|
||||
// TODO: SCSS of this directory will alter the main page
|
||||
import AppDemoRoute from './routes/DemoRoute';
|
||||
import Settings from './pages/Settings';
|
||||
|
||||
setupIonicReact();
|
||||
|
||||
@@ -122,14 +107,7 @@ interface DispatchProps {
|
||||
|
||||
interface IonicAppProps extends StateProps, DispatchProps {}
|
||||
|
||||
const IonicApp: React.FC<IonicAppProps> = ({
|
||||
darkMode,
|
||||
schedule,
|
||||
setIsLoggedIn,
|
||||
setUsername,
|
||||
loadConfData,
|
||||
loadUserData,
|
||||
}) => {
|
||||
const IonicApp: React.FC<IonicAppProps> = ({ darkMode, schedule, setIsLoggedIn, setUsername, loadConfData, loadUserData }) => {
|
||||
useEffect(() => {
|
||||
loadUserData();
|
||||
loadConfData();
|
||||
@@ -153,98 +131,28 @@ const IonicApp: React.FC<IonicAppProps> = ({
|
||||
<AppRoute />
|
||||
|
||||
<Route path="/tabs" render={() => <MainTabs />} />
|
||||
<Route path={paths.DEMO_WEATHER_APP} render={() => <DemoWeatherApp />} />
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
|
||||
<AppDemoRoute />
|
||||
|
||||
{/* */}
|
||||
|
||||
<Route path={paths.DEMO_ACCORDION_TUTORIAL} render={() => <DemoAccordionTutorial />} />
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
{/* */}
|
||||
|
||||
<Route path={paths.DEMO_BANKING_UI} render={() => <DemoBankingUi />} />
|
||||
<Route
|
||||
path={paths.DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL}
|
||||
render={() => <DemoCapacitorGoogleMapsTutorial />}
|
||||
/>
|
||||
|
||||
<Route path={paths.DEMO_COLOR_TUTORIAL} render={() => <DemoColorTutorial />} />
|
||||
|
||||
{/*
|
||||
<Route path={paths.DEMO_ECOMMERCE_EXAMPLE} render={() => <DemoEcommerceExample />} />
|
||||
<Route path={paths.DEMO_FACEBOOK_CLONE} render={() => <DemoFacebookClone />} />
|
||||
<Route path={paths.DEMO_FAST_FOOD_APP} render={() => <DemoFastFoodApp />} />
|
||||
<Route path={paths.DEMO_FLOATING_TABS} render={() => <DemoFloatingTabs />} />
|
||||
<Route path={paths.DEMO_INSTAGRAM_CLONE} render={() => <DemoInstagramClone />} />
|
||||
<Route path={paths.DEMO_KANBAN_BOARD} render={() => <DemoKanbanBoard />} />
|
||||
<Route path={paths.DEMO_ORDERING_APP} render={() => <DemoOrderingApp />} />
|
||||
<Route path={paths.DEMO_PROFILE_EXAMPLE} render={() => <DemoProfileExample />} />
|
||||
<Route path={paths.DEMO_PULLSTATE_TUTORIAL} render={() => <DemoPullstateTutorial />} />
|
||||
<Route path={paths.DEMO_REACT_ADD_TO_CART} render={() => <DemoReactAddToCart />} />
|
||||
<Route path={paths.DEMO_REACT_CALCULATOR} render={() => <DemoReactCalculator />} />
|
||||
<Route path={paths.DEMO_REACT_DRAWING_CANVAS} render={() => <DemoReactDrawingCanvas />} />
|
||||
<Route path={paths.DEMO_REACT_HOOK_FORM_EXAMPLE} render={() => <DemoReactHookFormExample />} />
|
||||
<Route path={paths.DEMO_REACT_ITEM_LIST} render={() => <DemoReactItemList />} />
|
||||
<Route path={paths.DEMO_REACT_LIFECYCLES} render={() => <DemoReactLifecycles />} />
|
||||
<Route path={paths.DEMO_REACT_LOGIN} render={() => <DemoReactLogin />} />
|
||||
<Route path={paths.DEMO_REACT_MARVEL_APP} render={() => <DemoReactMarvelApp />} />
|
||||
<Route path={paths.DEMO_REACT_MOVIE_APP_WITH_ALGOLIA} render(() => <DemoReactMovieAppWithAlgolia />} />
|
||||
<Route path={paths.DEMO_REACT_NOTES} render={() => <DemoReactNotes />} />
|
||||
<Route path={paths.DEMO_REACT_ONBOARDING_UI} render={() => <DemoReactOnboardingUI />} />
|
||||
<Route path={paths.DEMO_REACT_PROFILE_DASHBOARD_UI} render(() => <DemoReactProfileDashboardUI />} />
|
||||
<Route path={paths.DEMO_REACT_QR_CODE} render={() => <DemoReactQRCode />} />
|
||||
<Route path={paths.DEMO_REACT_QUOTES} render(() => <DemoReactQuotes />} />
|
||||
<Route path={paths.DEMO_REACT_SHOP_UI} render(() => <DemoReactShopUI />} />
|
||||
<Route path={paths.DEMO_REACT_TABS_MENUS_CUSTOM} render(() => <DemoReactTabsMenusCustom />} />
|
||||
<Route path={paths.DEMO_REACT_THEME_SWITCHER} render(() => <DemoReactThemeSwitcher />} />
|
||||
<Route path={paths.DEMO_REACT_WHATSAPP_CLONE} render(() => <DemoReactWhatsAppClone />} />
|
||||
<Route path={paths.DEMO_SKELETON_TEXT} render(() => <DemoSkeletonText />} />
|
||||
<Route path={paths.DEMO_STICKY_BOTTOM_SHEET_EXAMPLE} render(() => <DemoStickyBottomSheetExample />} />
|
||||
<Route path={paths.DEMO_STORAGE_EXAMPLE} render(() => <DemoStorageExample />} />
|
||||
<Route path={paths.DEMO_SWIPERJS_TUTORIAL} render(() => <DemoSwiperjsTutorial />} />
|
||||
<Route path={paths.DEMO_WEATHER_APP_UI} render(() => <DemoWeatherAppUI />} />
|
||||
*/}
|
||||
|
||||
<Route path={paths.DEMO_2FA_EXAMPLE} render={() => <Demo2FaExample />} />
|
||||
|
||||
{/* have problemx` */}
|
||||
{/* <Route path={paths.DEMO_REACT_WHATSAPP_CLONE} render={() => <DemoReactWhatsAppClone />} /> */}
|
||||
|
||||
<Route path={paths.DEMO_REACT_POLL_APP} render={() => <DemoReactPollApp />} />
|
||||
|
||||
<Route path={paths.DEMO_BLOG_POST_UI} render={() => <DemoBlogPostUi />} />
|
||||
<Route path={paths.DEMO_CLUB_HOUSE} render={() => <DemoClubHouse />} />
|
||||
<Route path={paths.DEMO_DICTIONARY_APP} render={() => <DemoDictionaryApp />} />
|
||||
<Route
|
||||
path={paths.DEMO_PINTEREST_FLOATING_TAB_BAR}
|
||||
render={() => <DemoPinterestFloatingTabBar />}
|
||||
/>
|
||||
<Route path={paths.DEMO_QR_SCANNER} render={() => <DemoQrScanner />} />
|
||||
<Route path={paths.DEMO_QUIZ_APP} render={() => <DemoQuizApp />} />
|
||||
<Route path={paths.DEMO_QUOTE_APP} render={() => <DemoQuoteApp />} />
|
||||
<Route path={paths.DEMO_REACT_OVERLAY_HOOKS} render={() => <DemoReactOverlayHooks />} />
|
||||
<Route path={paths.DEMO_REACT_POLL_APP} render={() => <DemoReactPollApp />} />
|
||||
{/* TODO: need update to @capacitor/barcode-scanner */}
|
||||
{/* <Route path={paths.DEMO_REACT_QR_CODE} render={() => <DemoReactQrCode />} /> */}
|
||||
|
||||
{/* TODO: resume DemoReactShop */}
|
||||
{/* <Route path={paths.DEMO_REACT_SHOP} render={() => <DemoReactShop />} /> */}
|
||||
|
||||
<Route path={paths.DEMO_REACT_SWITCH_TABS} render={() => <DemoReactSwitchTabs />} />
|
||||
<Route path={paths.DEMO_REACT_TRAVEL_APP} render={() => <DemoReactTravelApp />} />
|
||||
<Route path={paths.DEMO_RECIPE_APP} render={() => <DemoRecipeApp />} />
|
||||
<Route path={paths.DEMO_RESTAURANT_FINDER} render={() => <DemoRestaurantFinder />} />
|
||||
<Route path={paths.DEMO_SCORE_BOARD} render={() => <DemoScoreBoard />} />
|
||||
<Route path={paths.DEMO_SHOP_APP_UI} render={() => <DemoShopAppUi />} />
|
||||
<Route path={paths.DEMO_SLIDING_PROFILE} render={() => <DemoSlidingProfile />} />
|
||||
{/* have problemx` */}
|
||||
{/* <Route path={paths.DEMO_REACT_WHATSAPP_CLONE} render={() => <DemoReactWhatsAppClone />} /> */}
|
||||
|
||||
{/* TODO: scss will alter the main page, */}
|
||||
{/* <Route path={paths.DEMO_REACT_MARVEL_APP} render={() => <DemoReactMarvelApp />} /> */}
|
||||
|
||||
{/* TODO: update ionslide */}
|
||||
{/* <Route path={paths.DEMO_REACT_ONBOARDING_UI} render={() => <DemoReactOnboardingUI />} /> */}
|
||||
|
||||
{/* TODO: place a whatsapp clone */}
|
||||
{/* <Route path={paths.DEMO_REACT_WHATSAPP_CLONE} render={() => <DemoReactWhatsAppClone />} /> */}
|
||||
|
||||
<Route path="/account" component={Account} />
|
||||
<Route path="/login" component={Login} />
|
||||
@@ -253,6 +161,7 @@ const IonicApp: React.FC<IonicAppProps> = ({
|
||||
<Route path="/signup" component={Signup} />
|
||||
<Route path="/support" component={Support} />
|
||||
<Route path="/tutorial" component={Tutorial} />
|
||||
<Route path="/settings" component={Settings} />
|
||||
|
||||
<Route
|
||||
path="/logout"
|
||||
|
@@ -6,7 +6,7 @@ import { Route } from 'react-router';
|
||||
import NotImplemented from './pages/NotImplemented';
|
||||
import EventDetail from './pages/EventDetail';
|
||||
import MemberProfile from './pages/MemberProfile';
|
||||
import paths from './paths';
|
||||
import PATHS from './PATHS';
|
||||
import Settings from './pages/Settings';
|
||||
import ChangeLanguage from './pages/ChangeLanguage';
|
||||
import ServiceAgreement from './pages/ServiceAgreement';
|
||||
@@ -28,10 +28,10 @@ const AppRoute: React.FC = () => {
|
||||
{/* <Route path="/tabs/speakers/:id" component={SpeakerDetail} exact={true} /> */}
|
||||
|
||||
{/* */}
|
||||
<Route exact={true} path={paths.SETTINGS} component={Settings} />
|
||||
<Route exact={true} path={paths.CHANGE_LANGUAGE} component={ChangeLanguage} />
|
||||
<Route exact={true} path={paths.SERVICE_AGREEMENT} component={ServiceAgreement} />
|
||||
<Route exact={true} path={paths.PRIVACY_AGREEMENT} component={PrivacyAgreement} />
|
||||
<Route exact={true} path={PATHS.SETTINGS} component={Settings} />
|
||||
<Route exact={true} path={PATHS.CHANGE_LANGUAGE} component={ChangeLanguage} />
|
||||
<Route exact={true} path={PATHS.SERVICE_AGREEMENT} component={ServiceAgreement} />
|
||||
<Route exact={true} path={PATHS.PRIVACY_AGREEMENT} component={PrivacyAgreement} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
81
03_source/mobile/src/PATHS.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
const PATHS = {
|
||||
NOT_IMPLEMENTED: '/not_implemented',
|
||||
SETTINGS: '/settings',
|
||||
CHANGE_LANGUAGE: '/change_language',
|
||||
SERVICE_AGREEMENT: '/service_agreement',
|
||||
PRIVACY_AGREEMENT: '/privacy_agreement',
|
||||
SIGN_IN: '/mylogin',
|
||||
//
|
||||
ORDER_DETAIL: '/order_detail/:id',
|
||||
getOrderDetail: (id: string) => `/order_detail/${id}`,
|
||||
//
|
||||
TAB_NOT_IMPLEMENTED: '/tabs/not_implemented',
|
||||
EVENT_LIST: `/tabs/events`,
|
||||
MESSAGE_LIST: `/tabs/messages`,
|
||||
NEARBY_LIST: '/tabs/nearby',
|
||||
ORDERS_LIST: '/tabs/orders',
|
||||
FAVOURITES_LIST: `/tabs/favourites`,
|
||||
PROFILE: '/tabs/my_profile',
|
||||
|
||||
//
|
||||
// DEMO_WEATHER_APP: '/demo-weather-app',
|
||||
DEMO_WEATHER_APP_UI: '/demo-weather-app-ui',
|
||||
//
|
||||
|
||||
DEMO_2FA_EXAMPLE: '/demo-2fa-example',
|
||||
DEMO_ACCORDION_TUTORIAL: '/demo-accordion-tutorial',
|
||||
DEMO_BANKING_UI: '/demo-banking-ui',
|
||||
DEMO_BLOG_POST_UI: '/demo-blog-post-ui',
|
||||
DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL: '/demo-capacitor-google-maps-tutorial',
|
||||
DEMO_CLUB_HOUSE: '/demo-club-house',
|
||||
DEMO_COLOR_TUTORIAL: '/demo-color-tutorial',
|
||||
DEMO_DICTIONARY_APP: '/demo-dictionary-app',
|
||||
DEMO_ECOMMERCE_EXAMPLE: '/demo-ecommerce-example',
|
||||
DEMO_FACEBOOK_CLONE: '/demo-facebook-clone',
|
||||
DEMO_FAST_FOOD_APP: '/demo-fast-food-app',
|
||||
DEMO_FLOATING_TABS: '/demo-floating-tabs',
|
||||
DEMO_INSTAGRAM_CLONE: '/demo-instagram-clone',
|
||||
DEMO_KANBAN_BOARD: '/demo-kanban-board',
|
||||
DEMO_ORDERING_APP: '/demo-ordering-app',
|
||||
DEMO_PAGE: '/tabs/demo-list',
|
||||
DEMO_PINTEREST_FLOATING_TAB_BAR: '/demo-pinterest-floating-tab-bar',
|
||||
DEMO_PROFILE_EXAMPLE: '/demo-profile-example',
|
||||
DEMO_PULLSTATE_TUTORIAL: '/demo-pullstate-tutorial',
|
||||
DEMO_QUIZ_APP: '/demo-quiz-app',
|
||||
DEMO_QUOTE_APP: '/demo-quote-app',
|
||||
DEMO_REACT_ADD_TO_CART: '/demo-react-add-to-cart',
|
||||
DEMO_REACT_CALCULATOR: '/demo-react-calculator',
|
||||
DEMO_REACT_DRAWING_CANVAS: '/demo-react-drawing-canvas',
|
||||
DEMO_REACT_HOOK_FORM_EXAMPLE: '/demo-react-hook-form-example',
|
||||
DEMO_REACT_ITEM_LIST: '/demo-react-item-list',
|
||||
DEMO_REACT_LIFECYCLES: '/demo-react-lifecycles',
|
||||
DEMO_REACT_LOGIN: '/demo-react-login',
|
||||
DEMO_REACT_MARVEL_APP: '/demo-react-marvel-app',
|
||||
DEMO_REACT_MOVIE_APP_WITH_ALGOLIA: '/demo-react-movie-app-with-algolia',
|
||||
DEMO_REACT_NOTES: '/demo-react-notes',
|
||||
DEMO_REACT_ONBOARDING_UI: '/demo-react-onboarding-ui',
|
||||
DEMO_REACT_OVERLAY_HOOKS: '/demo-react-overlay-hooks',
|
||||
DEMO_REACT_POLL_APP: '/demo-react-poll-app',
|
||||
DEMO_REACT_PROFILE_DASHBOARD_UI: '/demo-react-profile-dashboard-ui',
|
||||
|
||||
DEMO_REACT_QR_CODE: '/demo-react-qr-code',
|
||||
DEMO_QR_SCANNER: '/demo-qr-scanner',
|
||||
|
||||
DEMO_REACT_QUOTES: '/demo-react-quotes',
|
||||
DEMO_REACT_SHOP_UI: '/demo-react-shop-ui',
|
||||
DEMO_REACT_SHOP: '/demo-react-shop',
|
||||
DEMO_REACT_SWITCH_TABS: '/demo-react-switch-tabs',
|
||||
DEMO_REACT_TABS_MENUS_CUSTOM: '/demo-react-tabs-menus-custom',
|
||||
DEMO_REACT_THEME_SWITCHER: '/demo-react-theme-switcher',
|
||||
DEMO_REACT_TRAVEL_APP: '/demo-react-travel-app',
|
||||
DEMO_REACT_WHATSAPP_CLONE: '/demo-react-whatsapp-clone',
|
||||
DEMO_RECIPE_APP: '/demo-recipe-app',
|
||||
DEMO_RESTAURANT_FINDER: '/demo-restaurant-finder',
|
||||
DEMO_SCORE_BOARD: '/demo-score-board',
|
||||
DEMO_SHOP_APP_UI: '/demo-shop-app-ui',
|
||||
DEMO_SKELETON_TEXT: '/demo-skeleton-text',
|
||||
DEMO_SLIDING_PROFILE: '/demo-sliding-profile',
|
||||
DEMO_STICKY_BOTTOM_SHEET_EXAMPLE: '/demo-sticky-bottom-sheet-example',
|
||||
DEMO_STORAGE_EXAMPLE: '/demo-storage-example',
|
||||
};
|
||||
export default PATHS;
|
@@ -2,7 +2,7 @@ import { Route } from 'react-router';
|
||||
import NotImplemented from './pages/NotImplemented';
|
||||
import EventDetail from './pages/EventDetail';
|
||||
import MemberProfile from './pages/MemberProfile';
|
||||
import paths from './paths';
|
||||
import PATHS from './PATHS';
|
||||
import MembersNearByList from './pages/MembersNearByList';
|
||||
import OrderList from './pages/OrderList';
|
||||
import MessageList from './pages/MessageList';
|
||||
@@ -17,25 +17,25 @@ import DemoList from './pages/DemoList';
|
||||
const TabAppRoute: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<Route path={paths.TAB_NOT_IMPLEMENTED} component={NotImplemented} />
|
||||
<Route path={PATHS.TAB_NOT_IMPLEMENTED} component={NotImplemented} />
|
||||
|
||||
{/* */}
|
||||
<Route path={paths.NEARBY_LIST} render={() => <MembersNearByList />} exact={true} />
|
||||
<Route path={PATHS.NEARBY_LIST} render={() => <MembersNearByList />} exact={true} />
|
||||
|
||||
{/* */}
|
||||
<Route path={paths.ORDERS_LIST} render={() => <OrderList />} exact={true} />
|
||||
<Route path={PATHS.ORDERS_LIST} render={() => <OrderList />} exact={true} />
|
||||
|
||||
{/* */}
|
||||
<Route path={paths.MESSAGE_LIST} render={() => <MessageList />} exact={true} />
|
||||
<Route path={PATHS.MESSAGE_LIST} render={() => <MessageList />} exact={true} />
|
||||
|
||||
{/* */}
|
||||
<Route path={paths.FAVOURITES_LIST} render={() => <Favourites />} exact={true} />
|
||||
<Route path={PATHS.FAVOURITES_LIST} render={() => <Favourites />} exact={true} />
|
||||
|
||||
{/* */}
|
||||
<Route path={paths.EVENT_LIST} render={() => <EventList />} exact={true} />
|
||||
<Route path={PATHS.EVENT_LIST} render={() => <EventList />} exact={true} />
|
||||
|
||||
{/* */}
|
||||
<Route path={paths.PROFILE} render={() => <MyProfile />} exact={true} />
|
||||
<Route path={PATHS.PROFILE} render={() => <MyProfile />} exact={true} />
|
||||
|
||||
{/* */}
|
||||
<Route path="/tabs/demo-list" render={() => <DemoList />} exact={true} />
|
||||
|
@@ -3,7 +3,7 @@
|
||||
import axios from '../../lib/axios';
|
||||
|
||||
import { JWT_STORAGE_KEY } from './constant.js';
|
||||
import paths from '../../paths.js';
|
||||
import PATHS from '../../PATHS.js';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -60,7 +60,7 @@ export function tokenExpired(exp: number) {
|
||||
try {
|
||||
alert('Token expired!');
|
||||
sessionStorage.removeItem(JWT_STORAGE_KEY);
|
||||
window.location.href = paths.SIGN_IN;
|
||||
window.location.href = PATHS.SIGN_IN;
|
||||
} catch (error) {
|
||||
console.error('Error during token expiration:', error);
|
||||
throw error;
|
||||
|
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
@@ -1,62 +0,0 @@
|
||||
import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react';
|
||||
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => {
|
||||
const [property, setProperty] = useState(false);
|
||||
|
||||
const properties = {
|
||||
wind: {
|
||||
isIcon: false,
|
||||
icon: '/assets/WeatherDemo/wind.png',
|
||||
alt: 'wind',
|
||||
label: 'Wind',
|
||||
value: `${currentWeather.current.wind_mph}mph`,
|
||||
},
|
||||
feelsLike: {
|
||||
isIcon: true,
|
||||
icon: thermometerOutline,
|
||||
alt: 'feels like',
|
||||
label: 'Feels like',
|
||||
value: `${currentWeather.current.feelslike_c}°C`,
|
||||
},
|
||||
indexUV: {
|
||||
isIcon: true,
|
||||
icon: sunnyOutline,
|
||||
alt: 'index uv',
|
||||
label: 'Index UV',
|
||||
value: currentWeather.current.uv,
|
||||
},
|
||||
pressure: {
|
||||
isIcon: true,
|
||||
icon: pulseOutline,
|
||||
alt: 'pressure',
|
||||
label: 'Pressure',
|
||||
value: `${currentWeather.current.pressure_mb} mbar`,
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setProperty(properties[type]);
|
||||
}, [type]);
|
||||
|
||||
return (
|
||||
<IonCol size="6">
|
||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
||||
<IonCol size="3">
|
||||
{!property.isIcon && (
|
||||
<img alt={property.alt} src={property.icon} height="32" width="32" />
|
||||
)}
|
||||
{property.isIcon && (
|
||||
<IonIcon icon={property.icon} color="medium" style={{ fontSize: '2rem' }} />
|
||||
)}
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="9">
|
||||
<IonCardSubtitle>{property.label}</IonCardSubtitle>
|
||||
<IonNote>{property.value}</IonNote>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCol>
|
||||
);
|
||||
};
|
@@ -1,48 +0,0 @@
|
||||
import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react';
|
||||
import { WeatherProperty } from './WeatherProperty';
|
||||
|
||||
export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => (
|
||||
<IonGrid>
|
||||
<IonCard>
|
||||
<IonCardContent className="ion-text-center">
|
||||
<IonText color="primary">
|
||||
<h1>
|
||||
{currentWeather.location.region},{' '}
|
||||
<span style={{ color: 'gray' }}>{currentWeather.location.country}</span>
|
||||
</h1>
|
||||
</IonText>
|
||||
|
||||
<div className="ion-margin-top">
|
||||
<img
|
||||
alt="condition"
|
||||
src={currentWeather.current.condition.icon.replace('//', 'https://')}
|
||||
/>
|
||||
|
||||
<IonText color="dark">
|
||||
<h1 style={{ fontWeight: 'bold' }}>{currentWeather.current.condition.text}</h1>
|
||||
</IonText>
|
||||
|
||||
<IonText color="medium">
|
||||
<p>{new Date(currentWeather.location.localtime).toDateString()}</p>
|
||||
</IonText>
|
||||
</div>
|
||||
|
||||
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
|
||||
{currentWeather.current.temp_c}℃
|
||||
</IonCardTitle>
|
||||
|
||||
<IonGrid className="ion-margin-top">
|
||||
<IonRow>
|
||||
<WeatherProperty type="wind" currentWeather={currentWeather} />
|
||||
<WeatherProperty type="feelsLike" currentWeather={currentWeather} />
|
||||
</IonRow>
|
||||
|
||||
<IonRow className="ion-margin-top">
|
||||
<WeatherProperty type="indexUV" currentWeather={currentWeather} />
|
||||
<WeatherProperty type="pressure" currentWeather={currentWeather} />
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
</IonGrid>
|
||||
);
|
@@ -1,117 +0,0 @@
|
||||
import {
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonCol,
|
||||
IonGrid,
|
||||
IonIcon,
|
||||
IonNote,
|
||||
IonRow,
|
||||
IonSkeletonText,
|
||||
IonText,
|
||||
IonThumbnail,
|
||||
} from '@ionic/react';
|
||||
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
|
||||
|
||||
export const SkeletonDashboard = () => (
|
||||
<IonGrid>
|
||||
<IonCard>
|
||||
<IonCardContent className="ion-text-center">
|
||||
<IonText color="primary">
|
||||
<h1>
|
||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
||||
</h1>
|
||||
</IonText>
|
||||
|
||||
<div className="ion-margin-top">
|
||||
<IonThumbnail>
|
||||
<IonSkeletonText animated style={{ width: '2rem', height: '2rem' }} />
|
||||
</IonThumbnail>
|
||||
|
||||
<IonText color="dark">
|
||||
<h1 style={{ fontWeight: 'bold' }}>
|
||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
||||
</h1>
|
||||
</IonText>
|
||||
|
||||
<IonText color="medium">
|
||||
<p>
|
||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
||||
</p>
|
||||
</IonText>
|
||||
</div>
|
||||
|
||||
<IonCardTitle style={{ fontSize: '3rem' }} className="ion-margin-top">
|
||||
<IonSkeletonText animated style={{ height: '3rem', width: '30%', textAlign: 'center' }} />
|
||||
</IonCardTitle>
|
||||
|
||||
<IonGrid className="ion-margin-top">
|
||||
<IonRow>
|
||||
<IonCol size="6">
|
||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
||||
<IonCol size="3">
|
||||
<img alt="wind" src="/assets/WeatherDemo/wind.png" height="32" width="32" />
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="9">
|
||||
<IonCardSubtitle>Wind</IonCardSubtitle>
|
||||
<IonNote>
|
||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
||||
</IonNote>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="6">
|
||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
||||
<IonCol size="3">
|
||||
<IonIcon icon={thermometerOutline} color="medium" style={{ fontSize: '2rem' }} />
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="9">
|
||||
<IonCardSubtitle>Feels like</IonCardSubtitle>
|
||||
<IonNote>
|
||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
||||
</IonNote>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
|
||||
<IonRow className="ion-margin-top">
|
||||
<IonCol size="6">
|
||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
||||
<IonCol size="3">
|
||||
<IonIcon icon={sunnyOutline} color="medium" style={{ fontSize: '2rem' }} />
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="9">
|
||||
<IonCardSubtitle>Index UV</IonCardSubtitle>
|
||||
<IonNote>
|
||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
||||
</IonNote>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="6">
|
||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
||||
<IonCol size="3">
|
||||
<IonIcon icon={pulseOutline} color="medium" style={{ fontSize: '2rem' }} />
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="9">
|
||||
<IonCardSubtitle>Pressure</IonCardSubtitle>
|
||||
<IonNote>
|
||||
<IonSkeletonText animated style={{ height: '2rem', width: '90%' }} />
|
||||
</IonNote>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonGrid>
|
||||
</IonCardContent>
|
||||
</IonCard>
|
||||
</IonGrid>
|
||||
);
|
@@ -1,62 +0,0 @@
|
||||
import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react';
|
||||
import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => {
|
||||
const [property, setProperty] = useState(false);
|
||||
|
||||
const properties = {
|
||||
wind: {
|
||||
isIcon: false,
|
||||
icon: '/assets/WeatherDemo/wind.png',
|
||||
alt: 'wind',
|
||||
label: 'Wind',
|
||||
value: `${currentWeather.current.wind_mph}mph`,
|
||||
},
|
||||
feelsLike: {
|
||||
isIcon: true,
|
||||
icon: thermometerOutline,
|
||||
alt: 'feels like',
|
||||
label: 'Feels like',
|
||||
value: `${currentWeather.current.feelslike_c}°C`,
|
||||
},
|
||||
indexUV: {
|
||||
isIcon: true,
|
||||
icon: sunnyOutline,
|
||||
alt: 'index uv',
|
||||
label: 'Index UV',
|
||||
value: currentWeather.current.uv,
|
||||
},
|
||||
pressure: {
|
||||
isIcon: true,
|
||||
icon: pulseOutline,
|
||||
alt: 'pressure',
|
||||
label: 'Pressure',
|
||||
value: `${currentWeather.current.pressure_mb} mbar`,
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setProperty(properties[type]);
|
||||
}, [type]);
|
||||
|
||||
return (
|
||||
<IonCol size="6">
|
||||
<IonRow className="ion-justify-content-center ion-align-items-center">
|
||||
<IonCol size="3">
|
||||
{!property.isIcon && (
|
||||
<img alt={property.alt} src={property.icon} height="32" width="32" />
|
||||
)}
|
||||
{property.isIcon && (
|
||||
<IonIcon icon={property.icon} color="medium" style={{ fontSize: '2rem' }} />
|
||||
)}
|
||||
</IonCol>
|
||||
|
||||
<IonCol size="9">
|
||||
<IonCardSubtitle>{property.label}</IonCardSubtitle>
|
||||
<IonNote>{property.value}</IonNote>
|
||||
</IonCol>
|
||||
</IonRow>
|
||||
</IonCol>
|
||||
);
|
||||
};
|