Browse Source

feat: add new motion utilities and enhance UI components

master
sina_sajjadi 4 weeks ago
parent
commit
b1efc0ebf1
  1. 71
      package-lock.json
  2. 4
      package.json
  3. 44
      public/assets/images/Group 1000005169.svg
  4. 4
      public/assets/images/Group 1000005170.svg
  5. 4
      public/assets/images/Search.svg
  6. 4
      public/assets/images/Setting.svg
  7. 6
      public/assets/images/WhiteSetting.svg
  8. 5
      public/assets/images/hamburgure.svg
  9. 11
      public/assets/images/islamic-pattern3.svg
  10. 79
      src/components/context/font-setting-context.tsx
  11. 90
      src/components/context/ui.context.tsx
  12. 10
      src/components/layout/header.tsx
  13. 44
      src/components/layout/mobile-header.tsx
  14. 2
      src/components/layout/sidebar.tsx
  15. 30
      src/components/modals/modal-manager.tsx
  16. 117
      src/components/modals/modal.tsx
  17. 29
      src/components/modals/search-modal.tsx
  18. 6
      src/components/sidebar/list.tsx
  19. 4
      src/components/sidebar/tabs.tsx
  20. 25
      src/components/ui/check-box.tsx
  21. 40
      src/components/ui/download-app.tsx
  22. 39
      src/components/ui/footer-sticky.tsx
  23. 15
      src/components/ui/mobile-search.tsx
  24. 16
      src/components/ui/mobile-setting.tsx
  25. 53
      src/components/ui/range-input.tsx
  26. 199
      src/components/ui/setting.tsx
  27. 4
      src/components/utils/colorize-vowels.tsx
  28. 19
      src/pages/_app.tsx
  29. 2
      src/pages/_document.tsx
  30. 147
      src/pages/duas/[slug].tsx
  31. 2
      src/pages/last-reads.tsx
  32. 7
      src/styles/globals.css
  33. 18
      src/utils/motion/fade-in-left.ts
  34. 18
      src/utils/motion/fade-in-out.ts
  35. 18
      src/utils/motion/fade-in-right.ts
  36. 22
      src/utils/motion/fade-in-top.ts
  37. 18
      src/utils/motion/height-collapse.ts
  38. 18
      src/utils/motion/zoom-out-in.ts
  39. 27
      src/utils/use-click-outside.ts
  40. 7
      tailwind.config.ts

71
package-lock.json

@ -8,7 +8,11 @@
"name": "duas",
"version": "0.1.0",
"dependencies": {
"@reach/portal": "^0.18.0",
"axios": "^1.7.9",
"body-scroll-lock": "^4.0.0-beta.0",
"classnames": "^2.5.1",
"framer-motion": "^11.15.0",
"moment": "^2.30.1",
"moment-hijri": "^3.0.0",
"next": "15.1.0",
@ -825,6 +829,27 @@
"node": ">=14"
}
},
"node_modules/@reach/portal": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@reach/portal/-/portal-0.18.0.tgz",
"integrity": "sha512-TImozRapd576ofRk30Le2L3lRTFXF1p47B182wnp5eMTdZa74JX138BtNGEPJFOyrMaVmguVF8SSwZ6a0fon1Q==",
"dependencies": {
"@reach/utils": "0.18.0"
},
"peerDependencies": {
"react": "^16.8.0 || 17.x",
"react-dom": "^16.8.0 || 17.x"
}
},
"node_modules/@reach/utils": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.18.0.tgz",
"integrity": "sha512-KdVMdpTgDyK8FzdKO9SCpiibuy/kbv3pwgfXshTI6tEcQT1OOwj7BAksnzGC0rPz0UholwC+AgkqEl3EJX3M1A==",
"peerDependencies": {
"react": "^16.8.0 || 17.x",
"react-dom": "^16.8.0 || 17.x"
}
},
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@ -1475,6 +1500,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/body-scroll-lock": {
"version": "4.0.0-beta.0",
"resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz",
"integrity": "sha512-a7tP5+0Mw3YlUJcGAKUqIBkYYGlYxk2fnCasq/FUph1hadxlTRjF+gAcZksxANnaMnALjxEddmSi/H3OR8ugcQ=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -1644,6 +1674,11 @@
"node": ">= 6"
}
},
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
},
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@ -2713,6 +2748,32 @@
"node": ">= 6"
}
},
"node_modules/framer-motion": {
"version": "11.15.0",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.15.0.tgz",
"integrity": "sha512-MLk8IvZntxOMg7lDBLw2qgTHHv664bYoYmnFTmE0Gm/FW67aOJk0WM3ctMcG+Xhcv+vh5uyyXwxvxhSeJzSe+w==",
"dependencies": {
"motion-dom": "^11.14.3",
"motion-utils": "^11.14.3",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -3753,6 +3814,16 @@
"moment": "^2.30.1"
}
},
"node_modules/motion-dom": {
"version": "11.14.3",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.14.3.tgz",
"integrity": "sha512-lW+D2wBy5vxLJi6aCP0xyxTxlTfiu+b+zcpVbGVFUxotwThqhdpPRSmX8xztAgtZMPMeU0WGVn/k1w4I+TbPqA=="
},
"node_modules/motion-utils": {
"version": "11.14.3",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.14.3.tgz",
"integrity": "sha512-Xg+8xnqIJTpr0L/cidfTTBFkvRw26ZtGGuIhA94J9PQ2p4mEa06Xx7QVYZH0BP+EpMSaDlu+q0I0mmvwADPsaQ=="
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",

4
package.json

@ -9,7 +9,11 @@
"lint": "next lint"
},
"dependencies": {
"@reach/portal": "^0.18.0",
"axios": "^1.7.9",
"body-scroll-lock": "^4.0.0-beta.0",
"classnames": "^2.5.1",
"framer-motion": "^11.15.0",
"moment": "^2.30.1",
"moment-hijri": "^3.0.0",
"next": "15.1.0",

44
public/assets/images/Group 1000005169.svg

@ -0,0 +1,44 @@
<svg width="39" height="39" viewBox="0 0 39 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="39" height="39" rx="13.125" fill="url(#paint0_radial_8984_12881)"/>
<path d="M15.3991 6.80273H23.1373C28.1589 6.80273 32.1926 10.7657 32.1926 15.6992V23.3016C32.1926 28.2351 28.1589 32.1981 23.1373 32.1981H15.3991C10.3775 32.1981 6.34375 28.2351 6.34375 23.3016V15.6992C6.34375 10.8061 10.3775 6.80273 15.3991 6.80273Z" stroke="white" stroke-width="1.81395" stroke-miterlimit="10"/>
<path d="M21.0402 20.5381C20.759 20.4199 20.4753 20.7009 20.5909 20.9831L22.1732 24.8475C22.2139 24.9469 22.2989 25.0214 22.4029 25.0487L25.4113 25.8388C25.6628 25.9049 25.8932 25.6772 25.8301 25.425L25.0692 22.3815C25.0431 22.277 24.9693 22.1909 24.87 22.1491L21.0402 20.5381Z" fill="white"/>
<path d="M19.7402 10.7697C19.6082 10.5437 19.2815 10.5437 19.1495 10.7697L17.5692 13.4745C17.5157 13.566 17.5077 13.6771 17.5474 13.7754L19.0961 17.6015C19.2106 17.8845 19.6102 17.8872 19.7286 17.6058L21.339 13.7772C21.3808 13.678 21.3734 13.5649 21.3191 13.472L19.7402 10.7697Z" fill="white"/>
<path d="M24.9389 16.7367C25.0383 16.6968 25.1132 16.6126 25.1414 16.5093L25.9605 13.506C26.0294 13.2534 25.8005 13.0202 25.5467 13.0843L22.5442 13.8425C22.442 13.8684 22.3573 13.9399 22.3149 14.0364L20.6465 17.8283C20.523 18.1089 20.8026 18.3978 21.0871 18.2835L24.9389 16.7367Z" fill="white"/>
<path d="M21.4889 19.1848C21.2068 19.3012 21.2068 19.7008 21.4889 19.8172L25.3147 21.3961C25.4144 21.4373 25.5276 21.429 25.6202 21.3738L28.3253 19.7629C28.5503 19.629 28.5475 19.3023 28.3204 19.1722L25.6186 17.6239C25.5268 17.5713 25.4158 17.5642 25.318 17.6045L21.4889 19.1848Z" fill="white"/>
<path d="M13.3691 13.0124C13.1172 12.9463 12.8867 13.1746 12.9505 13.4271L13.7094 16.4323C13.7357 16.5364 13.8094 16.6222 13.9084 16.6638L17.7357 18.2737C18.0173 18.3922 18.3011 18.1102 18.1846 17.8279L16.6059 14.0027C16.565 13.9036 16.4802 13.8295 16.3766 13.8023L13.3691 13.0124Z" fill="white"/>
<path d="M17.51 25.226C17.4692 25.3247 17.4769 25.4369 17.5308 25.5291L19.1104 28.2326C19.2425 28.4586 19.5691 28.4586 19.7012 28.2326L21.2807 25.5291C21.3346 25.4369 21.3423 25.3247 21.3016 25.226L19.722 21.3986C19.6056 21.1165 19.206 21.1165 19.0896 21.3986L17.51 25.226Z" fill="white"/>
<path d="M13.9101 22.2652C13.8107 22.3051 13.7358 22.3893 13.7076 22.4926L12.8864 25.5034C12.818 25.7543 13.0435 25.9868 13.2963 25.9261L16.3376 25.1962C16.4426 25.171 16.5296 25.0976 16.5722 24.9983L18.2144 21.1664C18.3346 20.886 18.0555 20.6005 17.7725 20.7141L13.9101 22.2652Z" fill="white"/>
<path d="M17.3591 19.8177C17.6412 19.7013 17.6412 19.3017 17.3591 19.1853L13.5317 17.6057C13.4329 17.5649 13.3208 17.5727 13.2286 17.6265L10.5251 19.2061C10.2991 19.3382 10.2991 19.6648 10.5251 19.7969L13.2286 21.3764C13.3208 21.4303 13.4329 21.438 13.5317 21.3973L17.3591 19.8177Z" fill="white"/>
<g opacity="0.26">
<path d="M18.1523 12.4885C19.3211 12.6394 20.4144 13.0541 21.3947 13.6573V13.6196L19.4342 10.2642L18.1523 12.4885Z" fill="url(#paint1_linear_8984_12881)"/>
<path d="M24.9482 16.7404C25.0393 16.6979 25.1071 16.6174 25.1336 16.5204L25.9578 13.4985C26.0262 13.2476 25.8007 13.0151 25.5479 13.0758L22.5132 13.8041C22.4058 13.8299 22.3201 13.9109 22.2884 14.0167C22.2512 14.1406 22.2952 14.2742 22.396 14.3553C23.1809 14.9867 23.8537 15.7763 24.3681 16.6549C24.4599 16.8116 24.6551 16.8772 24.8197 16.8004L24.9482 16.7404Z" fill="url(#paint2_linear_8984_12881)"/>
<path d="M28.8194 19.4639L25.464 17.5034L24.8984 17.7296C25.3132 18.7476 25.5394 19.8409 25.5394 21.0097C25.5394 21.1228 25.5394 21.2359 25.5394 21.349L28.8194 19.4639Z" fill="url(#paint3_linear_8984_12881)"/>
<path d="M24.293 25.5332L25.9518 25.9856L25.2732 23.2334C25.047 24.0628 24.7454 24.8169 24.293 25.5332Z" fill="url(#paint4_linear_8984_12881)"/>
</g>
<defs>
<radialGradient id="paint0_radial_8984_12881" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(32.5 0.855258) rotate(129.662) scale(48.0775)">
<stop stop-color="#FFA850"/>
<stop offset="0.219104" stop-color="#FF6385"/>
<stop offset="0.433372" stop-color="#F961F0"/>
<stop offset="0.666519" stop-color="#488DFB"/>
<stop offset="0.787693" stop-color="#15B9FD"/>
<stop offset="0.965387" stop-color="#0CF2D8"/>
</radialGradient>
<linearGradient id="paint1_linear_8984_12881" x1="19.7198" y1="12.0006" x2="20.4836" y2="10.7101" gradientUnits="userSpaceOnUse">
<stop stop-color="#CC80D0"/>
<stop offset="1" stop-color="#F08BAE"/>
</linearGradient>
<linearGradient id="paint2_linear_8984_12881" x1="24.1039" y1="14.9861" x2="24.9961" y2="13.453" gradientUnits="userSpaceOnUse">
<stop stop-color="#CC80D0"/>
<stop offset="1" stop-color="#F08BAE"/>
</linearGradient>
<linearGradient id="paint3_linear_8984_12881" x1="26.794" y1="19.4714" x2="27.6316" y2="17.9611" gradientUnits="userSpaceOnUse">
<stop stop-color="#CC80D0"/>
<stop offset="1" stop-color="#F08BAE"/>
</linearGradient>
<linearGradient id="paint4_linear_8984_12881" x1="25.0949" y1="24.6419" x2="25.8002" y2="23.8902" gradientUnits="userSpaceOnUse">
<stop stop-color="#CC80D0"/>
<stop offset="1" stop-color="#F08BAE"/>
</linearGradient>
</defs>
</svg>

4
public/assets/images/Group 1000005170.svg

@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.8555 1.14355L1.00038 12.9986" stroke="#8B8B8B" stroke-linecap="round"/>
<path d="M1 1.14355L12.8551 12.9986" stroke="#8B8B8B" stroke-linecap="round"/>
</svg>

4
public/assets/images/Search.svg

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.9611 17.2798C7.52974 17.2798 4.76562 14.4958 4.76562 11.0398C4.76562 7.5838 7.52974 4.7998 10.9611 4.7998C14.3924 4.7998 17.1565 7.5838 17.1565 11.0398C17.1565 14.4958 14.3924 17.2798 10.9611 17.2798ZM10.9611 5.7598C8.05397 5.7598 5.71877 8.1118 5.71877 11.0398C5.71877 13.9678 8.05397 16.3198 10.9611 16.3198C13.8681 16.3198 16.2033 13.9678 16.2033 11.0398C16.2033 8.1118 13.8681 5.7598 10.9611 5.7598Z" fill="#111111" stroke="#111111"/>
<path d="M15.5762 15.0083L19.8558 19.3187L19.182 19.9974L14.9023 15.687L15.5762 15.0083Z" fill="#111111" stroke="#111111"/>
</svg>

4
public/assets/images/Setting.svg

@ -1,6 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1_8342_7611" fill="white">
<mask id="path-1-inside-1_8979_10428" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5397 3.02002C13.1603 3.02002 13.7635 3.27484 14.196 3.71774C14.6277 4.16325 14.866 4.7743 14.8478 5.39402C14.8496 5.53356 14.8955 5.69478 14.9787 5.83605C15.1165 6.07007 15.3341 6.23475 15.5924 6.30236C15.8507 6.3665 16.1211 6.33183 16.3508 6.19575C17.4602 5.56216 18.8721 5.9418 19.5057 7.04255L20.0457 7.97777C20.0595 8.0029 20.0717 8.02717 20.0821 8.0523C20.6559 9.13659 20.2728 10.5017 19.2067 11.1249C19.0515 11.2142 18.9258 11.339 18.8392 11.4898C18.7048 11.7229 18.6676 12.0003 18.7352 12.2551C18.8045 12.5151 18.97 12.731 19.2032 12.8644C19.7293 13.1669 20.1219 13.6766 20.2788 14.2651C20.4357 14.8527 20.3499 15.4907 20.0439 16.0168L19.4684 16.9754C18.8348 18.064 17.4229 18.441 16.3248 17.8066C16.1783 17.7225 16.0093 17.6766 15.8411 17.6722H15.8359C15.5854 17.6722 15.328 17.7788 15.1408 17.9652C14.951 18.155 14.847 18.4081 14.8487 18.6768C14.8426 19.95 13.8069 20.9797 12.5397 20.9797H11.4537C10.1804 20.9797 9.14469 19.9448 9.14469 18.6716C9.14296 18.5147 9.09789 18.3518 9.01382 18.2105C8.87774 17.973 8.65759 17.8031 8.40363 17.7355C8.15141 17.6679 7.87492 17.7052 7.64784 17.8369C7.10352 18.1403 6.463 18.214 5.87709 18.0493C5.29204 17.8837 4.78673 17.4816 4.49031 16.9485L3.94859 16.015C3.31501 14.916 3.69204 13.5076 4.78933 12.8731C5.10049 12.6937 5.29377 12.3591 5.29377 12.0003C5.29377 11.6415 5.10049 11.306 4.78933 11.1266C3.69117 10.4887 3.31501 9.07679 3.94773 7.97777L4.53538 7.01309C5.1603 5.92619 6.57308 5.5431 7.6747 6.17582C7.82465 6.26509 7.9876 6.31016 8.15314 6.31189C8.69312 6.31189 9.14469 5.86639 9.15336 5.31861C9.14989 4.71449 9.38825 4.13465 9.82248 3.69694C10.2585 3.26011 10.8374 3.02002 11.4537 3.02002H12.5397ZM12.5397 4.32013H11.4537C11.185 4.32013 10.9336 4.425 10.7438 4.61395C10.5549 4.80377 10.4517 5.05599 10.4535 5.32468C10.4353 6.59185 9.39951 7.612 8.14534 7.612C7.74318 7.60767 7.35574 7.49933 7.01945 7.29824C6.54708 7.02955 5.92996 7.1951 5.65347 7.67614L5.06669 8.64082C4.798 9.10713 4.96268 9.72338 5.44112 10.0016C6.15098 10.4124 6.59388 11.1786 6.59388 12.0003C6.59388 12.822 6.15098 13.5873 5.43939 13.999C4.96355 14.2746 4.79887 14.8874 5.07449 15.3641L5.6214 16.3071C5.75661 16.5507 5.97763 16.7266 6.23332 16.7986C6.48814 16.8696 6.76896 16.8393 7.00298 16.7093C7.34708 16.5073 7.74751 16.4025 8.14968 16.4025C8.34816 16.4025 8.54664 16.4276 8.74079 16.4796C9.32671 16.6374 9.83548 17.0291 10.1371 17.5552C10.333 17.8855 10.4413 18.2712 10.4448 18.6647C10.4448 19.228 10.8972 19.6796 11.4537 19.6796H12.5397C13.0936 19.6796 13.546 19.2306 13.5486 18.6768C13.5451 18.0649 13.7843 17.4833 14.222 17.0456C14.6537 16.614 15.2569 16.3548 15.8602 16.3721C16.2554 16.3817 16.6359 16.4883 16.9713 16.679C17.4541 16.9554 18.0704 16.7908 18.3495 16.3141L18.925 15.3546C19.0533 15.1336 19.0905 14.8562 19.022 14.6005C18.9544 14.3448 18.7846 14.1229 18.5557 13.9921C18.0201 13.6835 17.6379 13.186 17.4793 12.5897C17.3224 12.0046 17.4082 11.3658 17.7141 10.8397C17.9135 10.493 18.2056 10.201 18.5557 9.99987C19.0255 9.72511 19.1902 9.11059 18.9172 8.63215C18.9059 8.61308 18.8955 8.59315 18.8868 8.57235L18.3789 7.69174C18.1024 7.2107 17.4871 7.04515 17.0043 7.31991C16.4825 7.62847 15.8619 7.71688 15.2656 7.56C14.6702 7.40572 14.1709 7.02869 13.8598 6.49651C13.6604 6.16368 13.5521 5.77625 13.5486 5.38188C13.5564 5.08546 13.4524 4.81937 13.2634 4.62435C13.0754 4.4302 12.811 4.32013 12.5397 4.32013ZM12.0005 9.06474C13.6187 9.06474 14.9353 10.3822 14.9353 12.0004C14.9353 13.6186 13.6187 14.9343 12.0005 14.9343C10.3823 14.9343 9.06573 13.6186 9.06573 12.0004C9.06573 10.3822 10.3823 9.06474 12.0005 9.06474ZM12.0005 10.3649C11.0991 10.3649 10.3658 11.099 10.3658 12.0004C10.3658 12.9018 11.0991 13.6342 12.0005 13.6342C12.9019 13.6342 13.6352 12.9018 13.6352 12.0004C13.6352 11.099 12.9019 10.3649 12.0005 10.3649Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5397 3.02002C13.1603 3.02002 13.7635 3.27484 14.196 3.71774C14.6277 4.16325 14.866 4.7743 14.8478 5.39402C14.8496 5.53356 14.8955 5.69478 14.9787 5.83605C15.1165 6.07007 15.3341 6.23475 15.5924 6.30236C15.8507 6.3665 16.1211 6.33183 16.3508 6.19575C17.4602 5.56216 18.8721 5.9418 19.5057 7.04255L20.0457 7.97777C20.0595 8.0029 20.0717 8.02717 20.0821 8.0523C20.6559 9.13659 20.2728 10.5017 19.2067 11.1249C19.0515 11.2142 18.9258 11.339 18.8392 11.4898C18.7048 11.7229 18.6676 12.0003 18.7352 12.2551C18.8045 12.5151 18.97 12.731 19.2032 12.8644C19.7293 13.1669 20.1219 13.6766 20.2788 14.2651C20.4357 14.8527 20.3499 15.4907 20.0439 16.0168L19.4684 16.9754C18.8348 18.064 17.4229 18.441 16.3248 17.8066C16.1783 17.7225 16.0093 17.6766 15.8411 17.6722H15.8359C15.5854 17.6722 15.328 17.7788 15.1408 17.9652C14.951 18.155 14.847 18.4081 14.8487 18.6768C14.8426 19.95 13.8069 20.9797 12.5397 20.9797H11.4537C10.1804 20.9797 9.14469 19.9448 9.14469 18.6716C9.14296 18.5147 9.09789 18.3518 9.01382 18.2105C8.87774 17.973 8.65759 17.8031 8.40363 17.7355C8.15141 17.6679 7.87492 17.7052 7.64784 17.8369C7.10352 18.1403 6.463 18.214 5.87709 18.0493C5.29204 17.8837 4.78673 17.4816 4.49031 16.9485L3.94859 16.015C3.31501 14.916 3.69204 13.5076 4.78933 12.8731C5.10049 12.6937 5.29377 12.3591 5.29377 12.0003C5.29377 11.6415 5.10049 11.306 4.78933 11.1266C3.69117 10.4887 3.31501 9.07679 3.94773 7.97777L4.53538 7.01309C5.1603 5.92619 6.57308 5.5431 7.6747 6.17582C7.82465 6.26509 7.9876 6.31016 8.15314 6.31189C8.69312 6.31189 9.14469 5.86639 9.15336 5.31861C9.14989 4.71449 9.38825 4.13465 9.82248 3.69694C10.2585 3.26011 10.8374 3.02002 11.4537 3.02002H12.5397ZM12.5397 4.32013H11.4537C11.185 4.32013 10.9336 4.425 10.7438 4.61395C10.5549 4.80377 10.4517 5.05599 10.4535 5.32468C10.4353 6.59185 9.39951 7.612 8.14534 7.612C7.74318 7.60767 7.35574 7.49933 7.01945 7.29824C6.54708 7.02955 5.92996 7.1951 5.65347 7.67614L5.06669 8.64082C4.798 9.10713 4.96268 9.72338 5.44112 10.0016C6.15098 10.4124 6.59388 11.1786 6.59388 12.0003C6.59388 12.822 6.15098 13.5873 5.43939 13.999C4.96355 14.2746 4.79887 14.8874 5.07449 15.3641L5.6214 16.3071C5.75661 16.5507 5.97763 16.7266 6.23332 16.7986C6.48814 16.8696 6.76896 16.8393 7.00298 16.7093C7.34708 16.5073 7.74751 16.4025 8.14968 16.4025C8.34816 16.4025 8.54664 16.4276 8.74079 16.4796C9.32671 16.6374 9.83548 17.0291 10.1371 17.5552C10.333 17.8855 10.4413 18.2712 10.4448 18.6647C10.4448 19.228 10.8972 19.6796 11.4537 19.6796H12.5397C13.0936 19.6796 13.546 19.2306 13.5486 18.6768C13.5451 18.0649 13.7843 17.4833 14.222 17.0456C14.6537 16.614 15.2569 16.3548 15.8602 16.3721C16.2554 16.3817 16.6359 16.4883 16.9713 16.679C17.4541 16.9554 18.0704 16.7908 18.3495 16.3141L18.925 15.3546C19.0533 15.1336 19.0905 14.8562 19.022 14.6005C18.9544 14.3448 18.7846 14.1229 18.5557 13.9921C18.0201 13.6835 17.6379 13.186 17.4793 12.5897C17.3224 12.0046 17.4082 11.3658 17.7141 10.8397C17.9135 10.493 18.2056 10.201 18.5557 9.99987C19.0255 9.72511 19.1902 9.11059 18.9172 8.63215C18.9059 8.61308 18.8955 8.59315 18.8868 8.57235L18.3789 7.69174C18.1024 7.2107 17.4871 7.04515 17.0043 7.31991C16.4825 7.62847 15.8619 7.71688 15.2656 7.56C14.6702 7.40572 14.1709 7.02869 13.8598 6.49651C13.6604 6.16368 13.5521 5.77625 13.5486 5.38188C13.5564 5.08546 13.4524 4.81937 13.2634 4.62435C13.0754 4.4302 12.811 4.32013 12.5397 4.32013ZM12.0005 9.06474C13.6187 9.06474 14.9353 10.3822 14.9353 12.0004C14.9353 13.6186 13.6187 14.9343 12.0005 14.9343C10.3823 14.9343 9.06573 13.6186 9.06573 12.0004C9.06573 10.3822 10.3823 9.06474 12.0005 9.06474ZM12.0005 10.3649C11.0991 10.3649 10.3658 11.099 10.3658 12.0004C10.3658 12.9018 11.0991 13.6342 12.0005 13.6342C12.9019 13.6342 13.6352 12.9018 13.6352 12.0004C13.6352 11.099 12.9019 10.3649 12.0005 10.3649Z" fill="white" stroke="white" stroke-width="4" mask="url(#path-1-inside-1_8342_7611)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5397 3.02002C13.1603 3.02002 13.7635 3.27484 14.196 3.71774C14.6277 4.16325 14.866 4.7743 14.8478 5.39402C14.8496 5.53356 14.8955 5.69478 14.9787 5.83605C15.1165 6.07007 15.3341 6.23475 15.5924 6.30236C15.8507 6.3665 16.1211 6.33183 16.3508 6.19575C17.4602 5.56216 18.8721 5.9418 19.5057 7.04255L20.0457 7.97777C20.0595 8.0029 20.0717 8.02717 20.0821 8.0523C20.6559 9.13659 20.2728 10.5017 19.2067 11.1249C19.0515 11.2142 18.9258 11.339 18.8392 11.4898C18.7048 11.7229 18.6676 12.0003 18.7352 12.2551C18.8045 12.5151 18.97 12.731 19.2032 12.8644C19.7293 13.1669 20.1219 13.6766 20.2788 14.2651C20.4357 14.8527 20.3499 15.4907 20.0439 16.0168L19.4684 16.9754C18.8348 18.064 17.4229 18.441 16.3248 17.8066C16.1783 17.7225 16.0093 17.6766 15.8411 17.6722H15.8359C15.5854 17.6722 15.328 17.7788 15.1408 17.9652C14.951 18.155 14.847 18.4081 14.8487 18.6768C14.8426 19.95 13.8069 20.9797 12.5397 20.9797H11.4537C10.1804 20.9797 9.14469 19.9448 9.14469 18.6716C9.14296 18.5147 9.09789 18.3518 9.01382 18.2105C8.87774 17.973 8.65759 17.8031 8.40363 17.7355C8.15141 17.6679 7.87492 17.7052 7.64784 17.8369C7.10352 18.1403 6.463 18.214 5.87709 18.0493C5.29204 17.8837 4.78673 17.4816 4.49031 16.9485L3.94859 16.015C3.31501 14.916 3.69204 13.5076 4.78933 12.8731C5.10049 12.6937 5.29377 12.3591 5.29377 12.0003C5.29377 11.6415 5.10049 11.306 4.78933 11.1266C3.69117 10.4887 3.31501 9.07679 3.94773 7.97777L4.53538 7.01309C5.1603 5.92619 6.57308 5.5431 7.6747 6.17582C7.82465 6.26509 7.9876 6.31016 8.15314 6.31189C8.69312 6.31189 9.14469 5.86639 9.15336 5.31861C9.14989 4.71449 9.38825 4.13465 9.82248 3.69694C10.2585 3.26011 10.8374 3.02002 11.4537 3.02002H12.5397ZM12.5397 4.32013H11.4537C11.185 4.32013 10.9336 4.425 10.7438 4.61395C10.5549 4.80377 10.4517 5.05599 10.4535 5.32468C10.4353 6.59185 9.39951 7.612 8.14534 7.612C7.74318 7.60767 7.35574 7.49933 7.01945 7.29824C6.54708 7.02955 5.92996 7.1951 5.65347 7.67614L5.06669 8.64082C4.798 9.10713 4.96268 9.72338 5.44112 10.0016C6.15098 10.4124 6.59388 11.1786 6.59388 12.0003C6.59388 12.822 6.15098 13.5873 5.43939 13.999C4.96355 14.2746 4.79887 14.8874 5.07449 15.3641L5.6214 16.3071C5.75661 16.5507 5.97763 16.7266 6.23332 16.7986C6.48814 16.8696 6.76896 16.8393 7.00298 16.7093C7.34708 16.5073 7.74751 16.4025 8.14968 16.4025C8.34816 16.4025 8.54664 16.4276 8.74079 16.4796C9.32671 16.6374 9.83548 17.0291 10.1371 17.5552C10.333 17.8855 10.4413 18.2712 10.4448 18.6647C10.4448 19.228 10.8972 19.6796 11.4537 19.6796H12.5397C13.0936 19.6796 13.546 19.2306 13.5486 18.6768C13.5451 18.0649 13.7843 17.4833 14.222 17.0456C14.6537 16.614 15.2569 16.3548 15.8602 16.3721C16.2554 16.3817 16.6359 16.4883 16.9713 16.679C17.4541 16.9554 18.0704 16.7908 18.3495 16.3141L18.925 15.3546C19.0533 15.1336 19.0905 14.8562 19.022 14.6005C18.9544 14.3448 18.7846 14.1229 18.5557 13.9921C18.0201 13.6835 17.6379 13.186 17.4793 12.5897C17.3224 12.0046 17.4082 11.3658 17.7141 10.8397C17.9135 10.493 18.2056 10.201 18.5557 9.99987C19.0255 9.72511 19.1902 9.11059 18.9172 8.63215C18.9059 8.61308 18.8955 8.59315 18.8868 8.57235L18.3789 7.69174C18.1024 7.2107 17.4871 7.04515 17.0043 7.31991C16.4825 7.62847 15.8619 7.71688 15.2656 7.56C14.6702 7.40572 14.1709 7.02869 13.8598 6.49651C13.6604 6.16368 13.5521 5.77625 13.5486 5.38188C13.5564 5.08546 13.4524 4.81937 13.2634 4.62435C13.0754 4.4302 12.811 4.32013 12.5397 4.32013ZM12.0005 9.06474C13.6187 9.06474 14.9353 10.3822 14.9353 12.0004C14.9353 13.6186 13.6187 14.9343 12.0005 14.9343C10.3823 14.9343 9.06573 13.6186 9.06573 12.0004C9.06573 10.3822 10.3823 9.06474 12.0005 9.06474ZM12.0005 10.3649C11.0991 10.3649 10.3658 11.099 10.3658 12.0004C10.3658 12.9018 11.0991 13.6342 12.0005 13.6342C12.9019 13.6342 13.6352 12.9018 13.6352 12.0004C13.6352 11.099 12.9019 10.3649 12.0005 10.3649Z" fill="#111111" stroke="#111111" stroke-width="4" mask="url(#path-1-inside-1_8979_10428)"/>
</svg>

6
public/assets/images/WhiteSetting.svg

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1_8342_7611" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5397 3.02002C13.1603 3.02002 13.7635 3.27484 14.196 3.71774C14.6277 4.16325 14.866 4.7743 14.8478 5.39402C14.8496 5.53356 14.8955 5.69478 14.9787 5.83605C15.1165 6.07007 15.3341 6.23475 15.5924 6.30236C15.8507 6.3665 16.1211 6.33183 16.3508 6.19575C17.4602 5.56216 18.8721 5.9418 19.5057 7.04255L20.0457 7.97777C20.0595 8.0029 20.0717 8.02717 20.0821 8.0523C20.6559 9.13659 20.2728 10.5017 19.2067 11.1249C19.0515 11.2142 18.9258 11.339 18.8392 11.4898C18.7048 11.7229 18.6676 12.0003 18.7352 12.2551C18.8045 12.5151 18.97 12.731 19.2032 12.8644C19.7293 13.1669 20.1219 13.6766 20.2788 14.2651C20.4357 14.8527 20.3499 15.4907 20.0439 16.0168L19.4684 16.9754C18.8348 18.064 17.4229 18.441 16.3248 17.8066C16.1783 17.7225 16.0093 17.6766 15.8411 17.6722H15.8359C15.5854 17.6722 15.328 17.7788 15.1408 17.9652C14.951 18.155 14.847 18.4081 14.8487 18.6768C14.8426 19.95 13.8069 20.9797 12.5397 20.9797H11.4537C10.1804 20.9797 9.14469 19.9448 9.14469 18.6716C9.14296 18.5147 9.09789 18.3518 9.01382 18.2105C8.87774 17.973 8.65759 17.8031 8.40363 17.7355C8.15141 17.6679 7.87492 17.7052 7.64784 17.8369C7.10352 18.1403 6.463 18.214 5.87709 18.0493C5.29204 17.8837 4.78673 17.4816 4.49031 16.9485L3.94859 16.015C3.31501 14.916 3.69204 13.5076 4.78933 12.8731C5.10049 12.6937 5.29377 12.3591 5.29377 12.0003C5.29377 11.6415 5.10049 11.306 4.78933 11.1266C3.69117 10.4887 3.31501 9.07679 3.94773 7.97777L4.53538 7.01309C5.1603 5.92619 6.57308 5.5431 7.6747 6.17582C7.82465 6.26509 7.9876 6.31016 8.15314 6.31189C8.69312 6.31189 9.14469 5.86639 9.15336 5.31861C9.14989 4.71449 9.38825 4.13465 9.82248 3.69694C10.2585 3.26011 10.8374 3.02002 11.4537 3.02002H12.5397ZM12.5397 4.32013H11.4537C11.185 4.32013 10.9336 4.425 10.7438 4.61395C10.5549 4.80377 10.4517 5.05599 10.4535 5.32468C10.4353 6.59185 9.39951 7.612 8.14534 7.612C7.74318 7.60767 7.35574 7.49933 7.01945 7.29824C6.54708 7.02955 5.92996 7.1951 5.65347 7.67614L5.06669 8.64082C4.798 9.10713 4.96268 9.72338 5.44112 10.0016C6.15098 10.4124 6.59388 11.1786 6.59388 12.0003C6.59388 12.822 6.15098 13.5873 5.43939 13.999C4.96355 14.2746 4.79887 14.8874 5.07449 15.3641L5.6214 16.3071C5.75661 16.5507 5.97763 16.7266 6.23332 16.7986C6.48814 16.8696 6.76896 16.8393 7.00298 16.7093C7.34708 16.5073 7.74751 16.4025 8.14968 16.4025C8.34816 16.4025 8.54664 16.4276 8.74079 16.4796C9.32671 16.6374 9.83548 17.0291 10.1371 17.5552C10.333 17.8855 10.4413 18.2712 10.4448 18.6647C10.4448 19.228 10.8972 19.6796 11.4537 19.6796H12.5397C13.0936 19.6796 13.546 19.2306 13.5486 18.6768C13.5451 18.0649 13.7843 17.4833 14.222 17.0456C14.6537 16.614 15.2569 16.3548 15.8602 16.3721C16.2554 16.3817 16.6359 16.4883 16.9713 16.679C17.4541 16.9554 18.0704 16.7908 18.3495 16.3141L18.925 15.3546C19.0533 15.1336 19.0905 14.8562 19.022 14.6005C18.9544 14.3448 18.7846 14.1229 18.5557 13.9921C18.0201 13.6835 17.6379 13.186 17.4793 12.5897C17.3224 12.0046 17.4082 11.3658 17.7141 10.8397C17.9135 10.493 18.2056 10.201 18.5557 9.99987C19.0255 9.72511 19.1902 9.11059 18.9172 8.63215C18.9059 8.61308 18.8955 8.59315 18.8868 8.57235L18.3789 7.69174C18.1024 7.2107 17.4871 7.04515 17.0043 7.31991C16.4825 7.62847 15.8619 7.71688 15.2656 7.56C14.6702 7.40572 14.1709 7.02869 13.8598 6.49651C13.6604 6.16368 13.5521 5.77625 13.5486 5.38188C13.5564 5.08546 13.4524 4.81937 13.2634 4.62435C13.0754 4.4302 12.811 4.32013 12.5397 4.32013ZM12.0005 9.06474C13.6187 9.06474 14.9353 10.3822 14.9353 12.0004C14.9353 13.6186 13.6187 14.9343 12.0005 14.9343C10.3823 14.9343 9.06573 13.6186 9.06573 12.0004C9.06573 10.3822 10.3823 9.06474 12.0005 9.06474ZM12.0005 10.3649C11.0991 10.3649 10.3658 11.099 10.3658 12.0004C10.3658 12.9018 11.0991 13.6342 12.0005 13.6342C12.9019 13.6342 13.6352 12.9018 13.6352 12.0004C13.6352 11.099 12.9019 10.3649 12.0005 10.3649Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5397 3.02002C13.1603 3.02002 13.7635 3.27484 14.196 3.71774C14.6277 4.16325 14.866 4.7743 14.8478 5.39402C14.8496 5.53356 14.8955 5.69478 14.9787 5.83605C15.1165 6.07007 15.3341 6.23475 15.5924 6.30236C15.8507 6.3665 16.1211 6.33183 16.3508 6.19575C17.4602 5.56216 18.8721 5.9418 19.5057 7.04255L20.0457 7.97777C20.0595 8.0029 20.0717 8.02717 20.0821 8.0523C20.6559 9.13659 20.2728 10.5017 19.2067 11.1249C19.0515 11.2142 18.9258 11.339 18.8392 11.4898C18.7048 11.7229 18.6676 12.0003 18.7352 12.2551C18.8045 12.5151 18.97 12.731 19.2032 12.8644C19.7293 13.1669 20.1219 13.6766 20.2788 14.2651C20.4357 14.8527 20.3499 15.4907 20.0439 16.0168L19.4684 16.9754C18.8348 18.064 17.4229 18.441 16.3248 17.8066C16.1783 17.7225 16.0093 17.6766 15.8411 17.6722H15.8359C15.5854 17.6722 15.328 17.7788 15.1408 17.9652C14.951 18.155 14.847 18.4081 14.8487 18.6768C14.8426 19.95 13.8069 20.9797 12.5397 20.9797H11.4537C10.1804 20.9797 9.14469 19.9448 9.14469 18.6716C9.14296 18.5147 9.09789 18.3518 9.01382 18.2105C8.87774 17.973 8.65759 17.8031 8.40363 17.7355C8.15141 17.6679 7.87492 17.7052 7.64784 17.8369C7.10352 18.1403 6.463 18.214 5.87709 18.0493C5.29204 17.8837 4.78673 17.4816 4.49031 16.9485L3.94859 16.015C3.31501 14.916 3.69204 13.5076 4.78933 12.8731C5.10049 12.6937 5.29377 12.3591 5.29377 12.0003C5.29377 11.6415 5.10049 11.306 4.78933 11.1266C3.69117 10.4887 3.31501 9.07679 3.94773 7.97777L4.53538 7.01309C5.1603 5.92619 6.57308 5.5431 7.6747 6.17582C7.82465 6.26509 7.9876 6.31016 8.15314 6.31189C8.69312 6.31189 9.14469 5.86639 9.15336 5.31861C9.14989 4.71449 9.38825 4.13465 9.82248 3.69694C10.2585 3.26011 10.8374 3.02002 11.4537 3.02002H12.5397ZM12.5397 4.32013H11.4537C11.185 4.32013 10.9336 4.425 10.7438 4.61395C10.5549 4.80377 10.4517 5.05599 10.4535 5.32468C10.4353 6.59185 9.39951 7.612 8.14534 7.612C7.74318 7.60767 7.35574 7.49933 7.01945 7.29824C6.54708 7.02955 5.92996 7.1951 5.65347 7.67614L5.06669 8.64082C4.798 9.10713 4.96268 9.72338 5.44112 10.0016C6.15098 10.4124 6.59388 11.1786 6.59388 12.0003C6.59388 12.822 6.15098 13.5873 5.43939 13.999C4.96355 14.2746 4.79887 14.8874 5.07449 15.3641L5.6214 16.3071C5.75661 16.5507 5.97763 16.7266 6.23332 16.7986C6.48814 16.8696 6.76896 16.8393 7.00298 16.7093C7.34708 16.5073 7.74751 16.4025 8.14968 16.4025C8.34816 16.4025 8.54664 16.4276 8.74079 16.4796C9.32671 16.6374 9.83548 17.0291 10.1371 17.5552C10.333 17.8855 10.4413 18.2712 10.4448 18.6647C10.4448 19.228 10.8972 19.6796 11.4537 19.6796H12.5397C13.0936 19.6796 13.546 19.2306 13.5486 18.6768C13.5451 18.0649 13.7843 17.4833 14.222 17.0456C14.6537 16.614 15.2569 16.3548 15.8602 16.3721C16.2554 16.3817 16.6359 16.4883 16.9713 16.679C17.4541 16.9554 18.0704 16.7908 18.3495 16.3141L18.925 15.3546C19.0533 15.1336 19.0905 14.8562 19.022 14.6005C18.9544 14.3448 18.7846 14.1229 18.5557 13.9921C18.0201 13.6835 17.6379 13.186 17.4793 12.5897C17.3224 12.0046 17.4082 11.3658 17.7141 10.8397C17.9135 10.493 18.2056 10.201 18.5557 9.99987C19.0255 9.72511 19.1902 9.11059 18.9172 8.63215C18.9059 8.61308 18.8955 8.59315 18.8868 8.57235L18.3789 7.69174C18.1024 7.2107 17.4871 7.04515 17.0043 7.31991C16.4825 7.62847 15.8619 7.71688 15.2656 7.56C14.6702 7.40572 14.1709 7.02869 13.8598 6.49651C13.6604 6.16368 13.5521 5.77625 13.5486 5.38188C13.5564 5.08546 13.4524 4.81937 13.2634 4.62435C13.0754 4.4302 12.811 4.32013 12.5397 4.32013ZM12.0005 9.06474C13.6187 9.06474 14.9353 10.3822 14.9353 12.0004C14.9353 13.6186 13.6187 14.9343 12.0005 14.9343C10.3823 14.9343 9.06573 13.6186 9.06573 12.0004C9.06573 10.3822 10.3823 9.06474 12.0005 9.06474ZM12.0005 10.3649C11.0991 10.3649 10.3658 11.099 10.3658 12.0004C10.3658 12.9018 11.0991 13.6342 12.0005 13.6342C12.9019 13.6342 13.6352 12.9018 13.6352 12.0004C13.6352 11.099 12.9019 10.3649 12.0005 10.3649Z" fill="white" stroke="white" stroke-width="4" mask="url(#path-1-inside-1_8342_7611)"/>
</svg>

5
public/assets/images/hamburgure.svg

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.125 7.125H19.875" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
<path d="M4.125 12H19.875" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
<path d="M4.125 16.875H19.875" stroke="black" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
</svg>

11
public/assets/images/islamic-pattern3.svg
File diff suppressed because it is too large
View File

79
src/components/context/font-setting-context.tsx

@ -0,0 +1,79 @@
// context/FontSettingsContext.tsx
import React, {
createContext,
useContext,
useState,
useEffect,
ReactNode,
} from "react";
interface FontSettings {
arabic: boolean;
arabicRange: number;
translation: boolean;
translationRange: number;
transliteration: boolean;
transliterationRange: number;
}
interface FontSettingsContextProps {
fontSettings: FontSettings;
setFontSettings: React.Dispatch<React.SetStateAction<FontSettings>>;
}
const DEFAULT_FONT_SETTINGS: FontSettings = {
arabic: true,
arabicRange: 100,
translation: true,
translationRange: 100,
transliteration: true,
transliterationRange: 100,
};
const FontSettingsContext = createContext<FontSettingsContextProps | undefined>(
undefined
);
export const FontSettingsProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const [fontSettings, setFontSettings] = useState<FontSettings>(() => {
if (typeof window !== "undefined") {
const savedSettings = localStorage.getItem("font-setting");
if (savedSettings) {
try {
return JSON.parse(savedSettings);
} catch (error) {
console.error(
"Failed to parse font settings from localStorage:",
error
);
return DEFAULT_FONT_SETTINGS;
}
}
}
return DEFAULT_FONT_SETTINGS;
});
useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem("font-setting", JSON.stringify(fontSettings));
}
}, [fontSettings]);
return (
<FontSettingsContext.Provider value={{ fontSettings, setFontSettings }}>
{children}
</FontSettingsContext.Provider>
);
};
export const useFontSettingsContext = (): FontSettingsContextProps => {
const context = useContext(FontSettingsContext);
if (!context) {
throw new Error(
"useFontSettingsContext must be used within a FontSettingsProvider"
);
}
return context;
};

90
src/components/context/ui.context.tsx

@ -0,0 +1,90 @@
import React, { createContext, useContext, ReactNode } from "react";
export interface UIContextProps {
displaySidebar: boolean;
displayDownload: boolean;
displaySetting: boolean;
displayModal: boolean;
modalView: string | null;
toastMessage: string | null;
}
const initialState = {
displaySidebar: false,
displaySetting: false,
displayDownload: true,
displayModal: false,
modalView: null,
toastMessage: null,
};
type Action =
| { type: "OPEN_SIDEBAR" }
| { type: "CLOSE_SIDEBAR" }
| { type: "OPEN_SETTING" }
| { type: "CLOSE_SETTING" }
| { type: "CLOSE_DOWNLOAD" }
| { type: "OPEN_MODAL"; view: string }
| { type: "CLOSE_MODAL" }
| { type: "SET_TOAST_MESSAGE"; message: string };
const UIContext = createContext<UIContextProps | undefined>(undefined);
function uiReducer(state: typeof initialState, action: Action) {
switch (action.type) {
case "OPEN_SIDEBAR":
return { ...state, displaySidebar: true };
case "CLOSE_SIDEBAR":
return { ...state, displaySidebar: false };
case "OPEN_SETTING":
return { ...state, displaySetting: true };
case "CLOSE_SETTING":
return { ...state, displaySetting: false };
case "CLOSE_DOWNLOAD":
return { ...state, displayDownload: false };
case "OPEN_MODAL":
return { ...state, displayModal: true, modalView: action.view };
case "CLOSE_MODAL":
return { ...state, displayModal: false, modalView: null };
case "SET_TOAST_MESSAGE":
return { ...state, toastMessage: action.message };
default:
return state;
}
}
export const UIProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [state, dispatch] = React.useReducer(uiReducer, initialState);
const openSidebar = () => dispatch({ type: "OPEN_SIDEBAR" });
const closeSidebar = () => dispatch({ type: "CLOSE_SIDEBAR" });
const openSetting = () => dispatch({ type: "OPEN_SETTING" });
const closeSetting = () => dispatch({ type: "CLOSE_SETTING" });
const closeDownload = () => dispatch({ type: "CLOSE_DOWNLOAD" });
const openModal = (view: string) => {dispatch({ type: "OPEN_MODAL", view })};
const closeModal = () => dispatch({ type: "CLOSE_MODAL" });
const setToastMessage = (message: string) =>
dispatch({ type: "SET_TOAST_MESSAGE", message });
const value = {
...state,
openSidebar,
closeSidebar,
openModal,
closeModal,
setToastMessage,
closeDownload,
openSetting,
closeSetting,
};
return <UIContext.Provider value={value}>{children}</UIContext.Provider>;
};
export const useUI = (): UIContextProps => {
const context = useContext(UIContext);
if (context === undefined) {
throw new Error("useUI must be used within a UIProvider");
}
return context;
};

10
src/components/layout/header.tsx

@ -3,9 +3,11 @@ import Image from "next/image";
import Link from "next/link";
import LanguageSwitcher from "../language-switcher";
import SearchDuas from "../ui/search-duas";
import { useUI } from "../context/ui.context";
const Header = () => {
const {displayDownload} = useUI()
return (
<header className="w-full shadow-sm sticky top-0 bg-white hidden lg:flex z-10">
<header className={`w-full shadow-sm sticky top-0 bg-white hidden lg:flex z-10 ${displayDownload && "mt-[58px]"}`}>
<div className="max-w-[1440px] h-20 m-auto flex justify-between w-full items-center">
<div className="flex gap-11 h-full items-center">
<div>
@ -19,8 +21,10 @@ const Header = () => {
<li className="h-full flex items-center border-[#F4846F] hover:border-b hover:text-[#EB6E57] ">
<Link href={"/about"}>About us</Link>
</li>
<li className="h-full flex items-center border-[#F4846F] hover:border-b hover:text-[#EB6E57] ">
<Link href={"/last-reads"}>Last Read</Link>
<li className="">
<button className="bg-gradient-to-r from-[#F79B59] to-[#EB6E57] rounded-[28px] py-1 px-4">
<p className="text-white text-base font-semibold">Donate</p>
</button>
</li>
</ul>
</div>

44
src/components/layout/mobile-header.tsx

@ -0,0 +1,44 @@
import Image from "next/image";
import React from "react";
import Logo from "../../../public/assets/images/Hosseiniye.svg";
import HeaderImg from "../../../public/assets/images/islamic-pattern3.svg";
import Hamburegure from "../../../public/assets/images/hamburgure.svg";
import Link from "next/link";
import MobileSetting from "../ui/mobile-setting";
import { useRouter } from "next/router";
import MobileSearch from "../ui/mobile-search";
const MobileHeader: React.FC<MobileHeaderProps> = () => {
const router = useRouter();
console.log(router);
return (
<header className="lg:hidden">
<div className=" shadow-header bg-cover bg-center">
<div className="shadow-inner-header w-36 rounded-full m-auto">
{/* Content inside the inner div */}
</div>
<Image className="absolute w-full" src={HeaderImg} alt="Header" />
</div>
<div className="p-4 flex justify-between items-center">
<div className="flex">
<button className="p-1 bg-white rounded-[15px]">
<Image width={30} src={Hamburegure} alt="Hamburger" />
</button>
<div className="w-[38px]" />
</div>
<Link className="z-50" href={"/"}>
<Image src={Logo} alt="Du'as Logo" />
</Link>
<div className="flex gap-3">
{!!router.pathname.includes("duas") && <MobileSetting />}
<MobileSearch />
</div>
</div>
</header>
);
};
export default MobileHeader;

2
src/components/layout/sidebar.tsx

@ -13,7 +13,7 @@ function SideBar() {
}
return (
<aside
className={`w-full h-[calc(100vh-130px)] self-start overflow-auto rounded-3xl p-6 lg:max-w-[430px] lg:bg-[#F5F5F5] ${
className={`w-full lg:h-[calc(100vh-130px)] self-start overflow-auto rounded-3xl p-6 lg:max-w-[430px] lg:bg-[#F5F5F5] ${
slug && "hidden lg:block"
}`}
>

30
src/components/modals/modal-manager.tsx

@ -0,0 +1,30 @@
import { useUI } from "../context/ui.context";
import Modal from "./modal";
import dynamic from "next/dynamic";
// import Newsletter from "../newsletter";
const SettingModal = dynamic(() => import("@/components/ui/setting"));
const SearchModal = dynamic(() => import("@/components/modals/search-modal"));
// const SignUpForm = dynamic(() => import("@components/auth/sign-up-form"));
// const ForgetPasswordForm = dynamic(
// () => import("@components/auth/forget-password-form")
// );
// const ProductPopup = dynamic(() => import("@components/product/product-popup"));
// const ChatPopup = dynamic(() => import("@components/chat/chat-popup"));
const ManagedModal: React.FC = () => {
const { displayModal, closeModal, modalView } = useUI();
return (
<Modal rootClassName="bottom" variant="bottom" open={displayModal} onClose={closeModal}>
{modalView === "SETTING_VIEW" && <SettingModal />}
{modalView === "SEARCH_VIEW" && <SearchModal />}
{/* {modalView === "SIGN_UP_VIEW" && <SignUpForm />}
{modalView === "FORGET_PASSWORD" && <ForgetPasswordForm />}
{modalView === "PRODUCT_VIEW" && <ProductPopup />}
{modalView === "NEWSLETTER_VIEW" && <Newsletter />}
{modalView === "CHAT_VIEW" && <ChatPopup />} */}
</Modal>
);
};
export default ManagedModal;

117
src/components/modals/modal.tsx

@ -0,0 +1,117 @@
import { useRef, useEffect } from 'react';
import { Portal } from '@reach/portal';
import { motion, AnimatePresence } from 'framer-motion';
import {
disableBodyScroll,
enableBodyScroll,
clearAllBodyScrollLocks,
} from 'body-scroll-lock';
import cn from 'classnames';
import { IoClose } from 'react-icons/io5';
import { fadeInOut } from '@/utils/motion/fade-in-out';
import { zoomOutIn } from '@/utils/motion/zoom-out-in';
import { useUI } from '../context/ui.context';
import useOnClickOutside from '@/utils/use-click-outside';
import SettingModal from '../ui/setting';
type ModalProps = {
open?: boolean;
onClose: () => void;
rootClassName?: string;
useBlurBackdrop?: boolean;
containerClassName?: string;
variant?: 'center' | 'bottom';
};
type DivElementRef = React.MutableRefObject<HTMLDivElement>;
// variant based classes for modal root, container & close btn
const rootClasses = {
center: 'md:p-5',
bottom: ' pb-0',
};
const containerClasses = {
center: 'h-auto max-h-full top-1/2 -translate-y-1/2 rounded-lg',
bottom:
'h-full max-h-70vh bottom-0 ltr:rounded-tr-2xl rtl:rounded-tl-2xl ltr:rounded-tl-2xl rtl:rounded-tr-2xl',
};
const closeBtnClasses = {
center:
'-top-3 md:-top-4 ltr:-right-3 rtl:-left-3 ltr:md:-right-4 rtl:md:-left-4',
bottom:
'top-1/4 ltr:left-1/2 rtl:right-1/2 transform -translate-y-1/2 -translate-x-1/2',
};
export default function Modal({
children,
open,
rootClassName,
useBlurBackdrop,
containerClassName,
variant = 'center',
}: React.PropsWithChildren<ModalProps>) {
const { closeModal } = useUI();
const modalRootRef = useRef() as DivElementRef;
const modalInnerRef = useRef() as DivElementRef;
useOnClickOutside(modalInnerRef, () => closeModal());
useEffect(() => {
if (modalInnerRef.current) {
if (open) {
disableBodyScroll(modalInnerRef.current);
} else {
enableBodyScroll(modalInnerRef.current);
}
}
return () => {
clearAllBodyScrollLocks();
};
}, [open]);
console.log(children);
return (
<Portal>
<AnimatePresence>
{open && (
<motion.div
ref={modalRootRef}
key="modal"
initial="from"
animate="to"
exit="from"
variants={fadeInOut(0.25)}
className={cn(
'modal-root fixed bg-black bg-opacity-70 inset-0 z-50',
useBlurBackdrop && 'backdrop-filter backdrop-blur-sm',
rootClasses[variant],
rootClassName
)}
>
<motion.div
initial="from"
animate="to"
exit="from"
variants={zoomOutIn()}
className="relative w-full h-full mx-auto"
>
<div
className={cn(
'w-full absolute left-1/2 transform -translate-x-1/2 shadow-xl',
containerClasses[variant],
containerClassName
)}
>
<div
ref={modalInnerRef}
className="h-full overflow-y-auto rounded-lg"
>
{children}
</div>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</Portal>
);
}

29
src/components/modals/search-modal.tsx

@ -0,0 +1,29 @@
import Image from "next/image";
import React from "react";
import search from "../../../public/assets/images/Search.svg"
import { IoMdClose } from "react-icons/io";
const SearchModal: React.FC = () => {
console.log("fdasdfasfa");
return (
<div className="bg-white w-full h-full p-6">
<div className="bg-[#F5F5F5]">
<div className="flex justify-between">
<button className="p-3 rounded-2xl bg-white">
<IoMdClose size={18} />
</button>
<h3>Search</h3>
<div />
</div>
<div className="flex w-full h-12 p-2 bg-white rounded-2xl">
<label htmlFor="search-input" className="flex items-center w-full h-full">
<Image width={24} height={24} src={search} alt="Search" />
<input id="search-input" className="w-full h-full ml-2" type="text" placeholder="Search..." />
</label>
</div>
</div>
</div>
);
};
export default SearchModal;

6
src/components/sidebar/list.tsx

@ -187,7 +187,7 @@ const List: React.FC<ListProps> = ({ tab, path, setPath, data, setData }) => {
return (
<div
className="flex justify-between p-3 bg-white my-4 rounded-2xl"
className="flex justify-between p-3 bg-white my-4 rounded-2xl cursor-pointer"
key={category.id}
onClick={() => openCategory(category)}
>
@ -208,7 +208,7 @@ const List: React.FC<ListProps> = ({ tab, path, setPath, data, setData }) => {
return (
<div
className="flex justify-between p-3 bg-white my-4 rounded-2xl"
className="flex justify-between p-3 bg-white my-4 rounded-2xl cursor-pointer"
key={category.id}
onClick={() => openCategory(category)}
>
@ -224,7 +224,7 @@ const List: React.FC<ListProps> = ({ tab, path, setPath, data, setData }) => {
return (
<div
className="flex justify-between p-3 bg-white my-4 rounded-2xl"
className="flex justify-between p-3 bg-white my-4 rounded-2xl cursor-pointer"
key={dua.id}
onClick={() => openDua(dua)}
>

4
src/components/sidebar/tabs.tsx

@ -47,7 +47,7 @@ const Tabs = () => {
{path.length > 1 && (
<div>
<div className="flex items-center justify-between">
<div onClick={back} className="p-2 bg-white rounded-xl">
<div onClick={back} className="p-2 bg-white rounded-xl cursor-pointer">
<FaArrowLeft size="23" />
</div>
<p className="text-base font-bold">{path[1]?.name}</p>
@ -59,7 +59,7 @@ const Tabs = () => {
return (
<>
<span
className="text-xs text-[#8B8B8B] font-semibold"
className="text-xs text-[#8B8B8B] font-semibold cursor-pointer"
key={index}
onClick={() => handlePathClick(index)}
>

25
src/components/ui/check-box.tsx

@ -0,0 +1,25 @@
import React from 'react';
interface CheckBoxProps {
id: string;
checked: boolean;
onChange: () => void; // Changed to a simpler onChange
}
const CheckBox: React.FC<CheckBoxProps> = ({ id, checked, onChange }) => {
return (
<label className="relative inline-flex items-center cursor-pointer">
<input
id={id}
type="checkbox"
className="sr-only peer"
checked={checked}
onChange={onChange}
/>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none rounded-full dark:bg-gray-300 peer-checked:bg-[#F4846F]"></div>
<span className="absolute left-0.5 top-0.5 bg-white border border-gray-300 rounded-full w-5 h-5 transition-transform duration-200 transform peer-checked:translate-x-full"></span>
</label>
);
};
export default CheckBox;

40
src/components/ui/download-app.tsx

@ -0,0 +1,40 @@
import React from "react";
import Close from "../../../public/assets/images/Group 1000005170.svg";
import Habib from "../../../public/assets/images/Group 1000005169.svg";
import Image from "next/image";
import { useUI } from "../context/ui.context";
const DownloadApp: React.FC = () => {
const { closeDownload, displayDownload } = useUI();
console.log(displayDownload);
return (
<div
className={`fixed bottom-3 left-0 right-0 mx-3 p-2 justify-between items-center bg-white rounded-2xl shadow-lg z-50 lg:flex-row-reverse lg:px-11 lg:top-0 lg:bottom-auto lg:mx-0 lg:rounded-none ${
displayDownload ? "flex" : "hidden"
}`}
>
<button className="" onClick={closeDownload}>
<Image src={Close} alt="Close" />
</button>
<div className="flex gap-14 items-center">
<div className="flex items-center gap-2">
<Image src={Habib} alt="Habib" />
<div>
<p className="text-lg font-bold">Habib App</p>
<p className="text-[12px] font-semibold -mt-1">
For Better Experience
</p>
</div>
</div>
<button className="bg-black w-24 h-9 rounded-lg">
<p className="text-white font-Calibri">Download</p>
</button>
</div>
<div className="hidden lg:block"/>
</div>
);
};
export default DownloadApp;

39
src/components/ui/footer-sticky.tsx

@ -1,39 +0,0 @@
import Home from "../../../public/assets/images/HomeIcon.svg";
import About from "../../../public/assets/images/Frame 26952.svg";
import Search from "../../../public/assets/images/search-alt-svgrepo-com.svg";
import Menue from "../../../public/assets/images/dots-vertical-svgrepo-com.svg";
import Image from "next/image";
const FooterSticky = () => {
const Navigations = [
{
name: "Home",
icon: Home,
},
{
name: "Search",
icon: Search,
},
{
name: "About Us",
icon: About,
},
{
name: "Menue",
icon: Menue,
},
];
return (
<div className="sticky bottom-0 flex justify-between items-center lg:hidden">
{Navigations.map((item) => (
<div key={item.name} className="flex items-center flex-col">
<Image width={28} height={28} alt={item.name} src={item.icon} />
<p>{item.name}</p>
</div>
))}
</div>
);
};
export default FooterSticky;

15
src/components/ui/mobile-search.tsx

@ -0,0 +1,15 @@
import React from "react";
import Image from "next/image";
import Search from ".././../../public/assets/images/Search.svg";
import { useUI } from "../context/ui.context";
const MobileSearch: React.FC = () => {
const {openModal} = useUI()
return (
<button onClick={()=>{openModal("SEARCH_VIEW")}} className="p-1 bg-white rounded-[15px] z-50">
<Image width={30} src={Search} alt="Setting" />
</button>
);
};
export default MobileSearch;

16
src/components/ui/mobile-setting.tsx

@ -0,0 +1,16 @@
import React from 'react';
import Image from 'next/image';
import Setting from "../../../public/assets/images/Setting.svg";
import { useUI } from '../context/ui.context';
const MobileSetting: React.FC = () => {
const {openModal} = useUI()
return (
<button onClick={()=>{openModal("SETTING_VIEW")}} className="p-1 bg-white rounded-[15px] z-50">
<Image width={30} src={Setting} alt="Setting" />
</button>
);
};
export default MobileSetting;

53
src/components/ui/range-input.tsx

@ -0,0 +1,53 @@
// components/ui/range-input.tsx
import React from 'react';
interface RangeInputProps {
value: number;
onChange: (value: number) => void;
disabled?: boolean;
}
const RangeInput: React.FC<RangeInputProps> = ({ value, onChange, disabled = false }) => {
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onChange(Number(event.target.value));
};
return (
<div className="flex items-center">
<input
type="range"
min="80"
max="200"
value={value}
onChange={handleChange}
disabled={disabled}
className={`w-28 h-[2px] ${disabled ? 'bg-gray-400' : 'bg-[#F4846F]'} outline-none transition-opacity duration-150 ease-in-out mr-3`}
style={{
appearance: 'none',
opacity: disabled ? 0.5 : 1,
}}
/>
<span className="text-[#7D8394] text-xs font-normal w-8">{value}%</span>
<style jsx>{`
input[type='range']::-webkit-slider-thumb {
appearance: none;
width: 14px;
height: 14px;
background: #F4846F;
border-radius: 50%;
cursor: pointer;
}
input[type='range']::-moz-range-thumb {
width: 20px;
height: 20px;
background: #F4846F;
border-radius: 50%;
cursor: pointer;
}
`}</style>
</div>
);
};
export default RangeInput;

199
src/components/ui/setting.tsx

@ -0,0 +1,199 @@
// components/ui/setting.tsx
import React, { useEffect, useRef } from "react";
import { useUI } from "../context/ui.context";
import Close from "../../../public/assets/images/Group 1000005170.svg";
import Image from "next/image";
import CheckBox from "./check-box";
import RangeInput from "./range-input";
import { useFontSettingsContext } from "../context/font-setting-context";
interface ModalProps {
className?: string; // Made optional with default value
}
const SettingModal: React.FC<ModalProps> = ({ className = "" }) => {
const { displaySetting, closeSetting , modalView , closeModal} = useUI();
const modalRef = useRef<HTMLDivElement>(null);
const closeButtonRef = useRef<HTMLButtonElement>(null);
const previouslyFocusedElement = useRef<HTMLElement | null>(null);
const { fontSettings, setFontSettings } = useFontSettingsContext();
const modalClasses = (modalView === "SETTING_VIEW") ? "absolute w-full bottom-0" : ""
// Handle modal visibility and accessibility
useEffect(() => {
const handleOutsideClick = (event: MouseEvent) => {
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
closeSetting();
}
};
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") {
closeSetting();
}
};
if (displaySetting) {
previouslyFocusedElement.current = document.activeElement as HTMLElement;
document.addEventListener("mousedown", handleOutsideClick);
document.addEventListener("keydown", handleKeyDown);
// Prevent background scrolling
document.body.style.overflow = "hidden";
// Shift focus to close button
closeButtonRef.current?.focus();
} else {
document.removeEventListener("mousedown", handleOutsideClick);
document.removeEventListener("keydown", handleKeyDown);
document.body.style.overflow = "auto";
// Return focus to previously focused element
previouslyFocusedElement.current?.focus();
}
return () => {
document.removeEventListener("mousedown", handleOutsideClick);
document.removeEventListener("keydown", handleKeyDown);
document.body.style.overflow = "auto";
};
}, [displaySetting, closeSetting]);
console.log(
(!displaySetting || !(modalView === "SETTING_VIEW")) , displaySetting , modalView
);
if (!displaySetting && !(modalView === "SETTING_VIEW")) {
return null;
}
// Handlers for toggles
const handleArabicToggle = () => {
setFontSettings((prev) => ({
...prev,
arabic: !prev.arabic,
}));
// Additional logic if needed
};
const handleTranslationToggle = () => {
setFontSettings((prev) => ({
...prev,
translation: !prev.translation,
}));
// Additional logic if needed
};
const handleTransliterationToggle = () => {
setFontSettings((prev) => ({
...prev,
transliteration: !prev.transliteration,
}));
// Additional logic if needed
};
// Handlers for range inputs
const handleArabicRangeChange = (value: number) => {
setFontSettings((prev) => ({
...prev,
arabicRange: value,
}));
// Additional logic if needed
};
const handleTranslationRangeChange = (value: number) => {
setFontSettings((prev) => ({
...prev,
translationRange: value,
}));
// Additional logic if needed
};
const handleTransliterationRangeChange = (value: number) => {
setFontSettings((prev) => ({
...prev,
transliterationRange: value,
}));
// Additional logic if needed
};
return (
<div
ref={modalRef}
className={`flex flex-col z-50 bg-white text-black h-60 p-6 rounded-3xl justify-between ${modalClasses} ${className}`}
role="dialog"
aria-modal="true"
>
<div className="flex justify-between">
<p className="text-[#8B8B8B] text-sm font-bold">Settings</p>
<button
ref={closeButtonRef}
onClick={()=>{closeSetting(); closeModal()}}
className="focus:outline-none"
aria-label="Close Settings"
>
<Image src={Close} alt="Close" />
</button>
</div>
<div className="flex flex-col gap-6 justify-between mb-4 text-sm font-normal">
{/* Arabic Toggle */}
<div className="flex items-center justify-between">
<label htmlFor="arabic-toggle" className="text-gray-700 font-medium">
Arabic
</label>
<div className="flex gap-5 items-center">
<CheckBox
id="arabic-toggle"
checked={fontSettings.arabic}
onChange={handleArabicToggle}
/>
<RangeInput
value={fontSettings.arabicRange}
onChange={handleArabicRangeChange}
disabled={!fontSettings.arabic}
/>
</div>
</div>
{/* Translation Toggle */}
<div className="flex items-center justify-between">
<label htmlFor="translation-toggle" className="text-gray-700 font-medium">
Translation
</label>
<div className="flex gap-5 items-center">
<CheckBox
id="translation-toggle"
checked={fontSettings.translation}
onChange={handleTranslationToggle}
/>
<RangeInput
value={fontSettings.translationRange}
onChange={handleTranslationRangeChange}
disabled={!fontSettings.translation}
/>
</div>
</div>
{/* Transliteration Toggle */}
<div className="flex items-center justify-between">
<label htmlFor="transliteration-toggle" className="text-gray-700 font-medium">
Transliteration
</label>
<div className="flex gap-5 items-center">
<CheckBox
id="transliteration-toggle"
checked={fontSettings.transliteration}
onChange={handleTransliterationToggle}
/>
<RangeInput
value={fontSettings.transliterationRange}
onChange={handleTransliterationRangeChange}
disabled={!fontSettings.transliteration}
/>
</div>
</div>
</div>
</div>
);
};
export default SettingModal;

4
src/components/utils/colorize-vowels.tsx

@ -9,11 +9,11 @@ const colorizeVowels = (text: string) => {
return (
<div className="relative">
{/* Bottom layer: Full text with vowels in orange */}
<div className="absolute top-0 right-0 text-2xl text-[#EB6E57] font-[UthmanTaha]">
<div className="absolute top-0 right-0 text-[#EB6E57] font-[UthmanTaha]">
{normalizedText}
</div>
{/* Top layer: Text without vowels in black */}
<div className="relative text-black font-[UthmanTaha] text-2xl">
<div className="relative text-black font-[UthmanTaha]">
{removeDiacritics(normalizedText)}
</div>
</div>

19
src/pages/_app.tsx

@ -1,5 +1,10 @@
import { FontSettingsProvider } from "@/components/context/font-setting-context";
import { UIProvider } from "@/components/context/ui.context";
import Header from "@/components/layout/header";
import MobileHeader from "@/components/layout/mobile-header";
import SideBar from "@/components/layout/sidebar";
import ManagedModal from "@/components/modals/modal-manager";
import DownloadApp from "@/components/ui/download-app";
import FooterSticky from "@/components/ui/footer-sticky";
import "@/styles/globals.css";
import type { AppProps } from "next/app";
@ -7,17 +12,21 @@ import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
console.log(pageProps);
return (
<>
<FontSettingsProvider>
<UIProvider>
<Header />
<div className=" m-auto lg:p-6">
<div className=" flex m-auto max-w-[1440px] flex-col lg:flex-row gap-6 relative">
<MobileHeader />
<div className="m-auto lg:p-6">
<div className="flex m-auto max-w-[1440px] flex-col lg:flex-row lg:gap-6 relative">
<SideBar />
<main className={`w-full`}>
<Component {...pageProps} />
</main>
</div>
<DownloadApp />
</div>
<FooterSticky/>
</>
<ManagedModal />
</UIProvider>
</FontSettingsProvider>
);
}

2
src/pages/_document.tsx

@ -4,7 +4,7 @@ export default function Document() {
return (
<Html lang="en">
<Head />
<body className="antialiased text-black bg-[#EAEAEA]">
<body className="antialiased text-black bg-[#F5F5F5] lg:bg-[#EAEAEA]">
<Main />
<NextScript />
</body>

147
src/pages/duas/[slug].tsx

@ -1,15 +1,19 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
// components/DuaComponent.tsx
import React, { useEffect, useRef, useState, useCallback } from "react";
import Image from "next/image";
import SettingIcon from "../../../public/assets/images/Setting.svg";
import SettingIcon from "../../../public/assets/images/WhiteSetting.svg";
import PlayIcon from "../../../public/assets/images/🦆 icon _play_.svg";
import { useEffect, useRef, useState, useCallback } from "react";
import { useParams } from "next/navigation";
import http from "@/api/http";
import colorizeVowels from "@/components/utils/colorize-vowels";
//@ts-ignore
import { FaArrowLeft } from "react-icons/fa6";
import { useRouter } from "next/router";
import {DuaComponentProps } from "@/components/utils/types"
import { DuaComponentProps } from "@/components/utils/types";
import SettingModal from "@/components/ui/setting";
import { useUI } from "@/components/context/ui.context";
import { useFontSettingsContext } from "@/components/context/font-setting-context";
// Define the Dua interface
interface Dua {
id: number;
@ -40,11 +44,19 @@ interface AudiosResponse {
results: Audio[];
}
const DuaComponent: React.FC<DuaComponentProps> = ({ SelectedDua , setSelectedDua }) => {
const DuaComponent: React.FC<DuaComponentProps> = ({
SelectedDua,
setSelectedDua,
}) => {
const params = useParams();
const slug = (params?.slug as string) ?? SelectedDua;
console.log(SelectedDua);
const router = useRouter()
const router = useRouter();
const { openSetting } = useUI();
// Use the shared context for font settings
const { fontSettings } = useFontSettingsContext();
// State hooks
const [duaParts, setDuaParts] = useState<Dua[]>([]);
const [audios, setAudios] = useState<Audio[]>([]);
@ -81,16 +93,15 @@ const DuaComponent: React.FC<DuaComponentProps> = ({ SelectedDua , setSelectedDu
`web/mafatih/${id}/audios/`
);
setAudios(audioResponse.data.results);
} catch (error) {
console.error("Error fetching audios:", error);
// Optionally, set an error state here
}
};
fetchData();
}, [slug]);
// Play audio from a specific part
const playAudio = useCallback(
(part: Dua) => {
@ -101,7 +112,6 @@ const DuaComponent: React.FC<DuaComponentProps> = ({ SelectedDua , setSelectedDu
);
if (selectedAudio && audioRef.current) {
console.log("fdafads");
const startTime = selectedAudio.duration[0][0]; // Assuming in seconds
audioRef.current.currentTime = startTime;
audioRef.current.play().catch((error) => {
@ -112,6 +122,8 @@ const DuaComponent: React.FC<DuaComponentProps> = ({ SelectedDua , setSelectedDu
[audios]
);
console.log(fontSettings.arabicRange);
// Handle audio end to scroll to the next part
const handleAudioEnd = useCallback(() => {
if (!recitingPart) return;
@ -144,36 +156,34 @@ const DuaComponent: React.FC<DuaComponentProps> = ({ SelectedDua , setSelectedDu
setRecitingPart(currentRecitingPart);
}, [audios, duaParts]);
function processSlug(slug: string): string {
if (!slug) return '';
function processSlug(slug: string): string {
if (!slug) return "";
// Split the slug by "-"
const parts = slug.split('-');
const parts = slug.split("-");
// Remove the last word
if (parts.length === 0) return '';
if (parts.length === 0) return "";
parts.pop();
// Convert each word to PascalCase
const pascalCaseWords = parts.map(word =>
word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
const pascalCaseWords = parts.map(
(word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
);
// Join the words with spaces
const result = pascalCaseWords.join(' ');
const result = pascalCaseWords.join(" ");
return result;
}
const handleBack = ()=>{
const handleBack = () => {
if (SelectedDua) {
setSelectedDua("")
}else{
router.back()
setSelectedDua("");
} else {
router.back();
}
}
};
// Setup audio event listeners and progress monitoring
useEffect(() => {
@ -207,34 +217,42 @@ const DuaComponent: React.FC<DuaComponentProps> = ({ SelectedDua , setSelectedDu
if (!slug) {
return null; // Handling the case where slug is not available
}
console.log(audios , duaParts);
console.log(audios, duaParts);
return (
<div
className={` rounded-3xl overflow-y-auto flex-grow h-[calc(100vh-130px)] bg-[#F5F5F5] lg:p-6 lg:rounded-3xl ${
className={`rounded-3xl overflow-y-auto flex-grow h-[calc(100vh-130px)] lg:bg-[#F5F5F5] lg:p-6 lg:rounded-3xl ${
!slug && "hidden lg:flex"
}`}
>
<header className="flex justify-between items-center bg-gradient-to-r from-[#F79B59] to-[#EB6E57] p-6 rounded-3xl text-white">
<button onClick={handleBack} className="bg-white/20 rounded-2xl p-[11px] lg:hidden">
<FaArrowLeft size={18}/>
</button>
<p className="text-sm font-semibold">{processSlug(slug)}</p>
<div className="hidden justify-between items-center bg-gradient-to-r from-[#F79B59] to-[#EB6E57] p-6 rounded-3xl text-white lg:flex">
<button
className="p-2 bg-white/20 rounded-2xl"
aria-label="Settings"
// Add onClick handler if settings functionality is needed
onClick={handleBack}
className="bg-white/20 rounded-2xl p-[11px] lg:hidden"
>
<Image width={24} height={24} src={SettingIcon} alt="Settings" />
<FaArrowLeft size={18} />
</button>
</header>
<p className="text-sm font-semibold">{processSlug(slug)}</p>
<div className="relative">
<button
className="p-2 bg-white/20 rounded-2xl"
aria-label="Settings"
onClick={openSetting}
>
<Image width={24} height={24} src={SettingIcon} alt="Settings" />
</button>
<SettingModal className="w-96 absolute right-0" />
</div>
</div>
<div className="p-6">
{duaParts.map((item, index) => (
<div
key={item.id}
ref={(el) => { partRefs.current[index] = el; }}
ref={(el) => {
partRefs.current[index] = el;
}}
className="p-1 rounded-3xl my-4"
style={{
background:
@ -245,17 +263,36 @@ console.log(audios , duaParts);
>
<div className="p-3 bg-white rounded-3xl">
{item.text && (
<div className="mb-4 text-right">
<div
className={`mb-4 text-right ${!fontSettings.arabic && "hidden"}`}
style={{
fontSize: `${25 * (fontSettings.arabicRange / 100)}px`,
}}
>
{colorizeVowels(item.text)}
</div>
)}
{item.local_alpha && (
<p className="text-sm font-normal border-b mb-4 pb-4">
<p
className={`text-sm font-normal mb-4 ${!fontSettings.transliteration && "hidden"}`}
style={{
fontSize: `${
14 * (fontSettings.transliterationRange / 100)
}px`,
}}
>
{item.local_alpha}
</p>
)}
{item.translation && (
<p className="text-sm font-normal">{item.translation}</p>
<p
className={`text-sm font-normal border-t pt-4 ${!fontSettings.translation && "hidden"}`}
style={{
fontSize: `${14 * (fontSettings.translationRange / 100)}px`,
}}
>
{item.translation}
</p>
)}
{item.description && (
<p className="text-[#36363C] italic text-xs font-normal">
@ -265,14 +302,18 @@ console.log(audios , duaParts);
{/* Play button to start audio from specific time */}
{!!audios.length && (
<button
onClick={() => playAudio(item)}
className="mt-5 cursor-pointer"
aria-label={`Play audio for part ${index + 1}`}
>
<Image width={24} height={24} src={PlayIcon} alt="Play Audio" />
</button>
<button
onClick={() => playAudio(item)}
className="mt-5 cursor-pointer"
aria-label={`Play audio for part ${index + 1}`}
>
<Image
width={24}
height={24}
src={PlayIcon}
alt="Play Audio"
/>
</button>
)}
</div>
</div>

2
src/pages/last-reads.tsx

@ -32,7 +32,7 @@ const LastReads = () => {
>
{lastDuas?.map((dua: Dua) => (
<div
className="flex justify-between p-3 bg-white my-4 rounded-2xl"
className="flex justify-between p-3 bg-white my-4 rounded-2xl cursor-pointer"
key={dua.id}
onClick={() => openDua(dua)}
>

7
src/styles/globals.css

@ -14,13 +14,6 @@
font-display: swap;
src: url('/public/fonts/KFGQPC Uthman Taha Naskh Regular.ttf') format('truetype'); /* Correct file format */
}
@font-face {
font-family: 'Uthmantaha'; /* Give a unique and clear name to your custom font */
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('/public/fonts/KFGQPC Uthman Taha Naskh Regular.ttf') format('truetype'); /* Correct file format */
}
/* Apply the custom font globally */
body {

18
src/utils/motion/fade-in-left.ts

@ -0,0 +1,18 @@
export function fadeInLeft (duration:number = 0.3) {
return {
from: {
left: '-100%',
transition: {
type: 'easeInOut',
duration: duration,
}
},
to: {
left: 0,
transition: {
type: 'easeInOut',
duration: duration,
}
},
}
}

18
src/utils/motion/fade-in-out.ts

@ -0,0 +1,18 @@
export function fadeInOut (duration:number = 0.2) {
return {
from: {
opacity: 0,
transition: {
type: 'easeInOut',
duration: duration,
}
},
to: {
opacity: 1,
transition: {
type: 'easeInOut',
duration: duration,
}
},
}
}

18
src/utils/motion/fade-in-right.ts

@ -0,0 +1,18 @@
export function fadeInRight (duration:number = 0.3) {
return {
from: {
right: '-100%',
transition: {
type: 'easeInOut',
duration: duration,
}
},
to: {
right: 0,
transition: {
type: 'easeInOut',
duration: duration,
}
},
}
}

22
src/utils/motion/fade-in-top.ts

@ -0,0 +1,22 @@
export function fadeInTop (duration:number = 0.5) {
return {
from: {
position: 'relative',
top: "50px",
opacity: 0,
transition: {
type: 'easeInOut',
duration: duration,
}
},
to: {
position: 'relative',
top: "0",
opacity: 1,
transition: {
type: 'easeInOut',
duration: duration,
}
},
}
}

18
src/utils/motion/height-collapse.ts

@ -0,0 +1,18 @@
export function heightCollapse() {
return {
from: {
opacity: 0,
height: 0,
transition: {
ease: [0.04, 0.62, 0.23, 0.98],
},
},
to: {
opacity: 1,
height: "auto",
transition: {
ease: [0.04, 0.62, 0.23, 0.98],
},
},
};
}

18
src/utils/motion/zoom-out-in.ts

@ -0,0 +1,18 @@
export function zoomOutIn (duration:number = 0.2) {
return {
from: {
scale: 1.1,
transition: {
type: 'easeOut',
duration: duration,
}
},
to: {
scale: 1,
transition: {
type: 'easeOut',
duration: duration,
}
},
}
}

27
src/utils/use-click-outside.ts

@ -0,0 +1,27 @@
import { useEffect, RefObject } from 'react'
type Event = MouseEvent | TouchEvent
export default function useOnClickOutside<T extends HTMLElement = HTMLElement>(
ref: RefObject<T>,
handler: (event: Event) => void,
) {
useEffect(
() => {
const listener = (event: Event) => {
const el = ref?.current
if (!el || el.contains((event?.target as Node) || null)) {
return
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
},
[ref, handler]
);
}

7
tailwind.config.ts

@ -8,6 +8,13 @@ export default {
],
theme: {
extend: {
boxShadow: {
'header': '2px -10px 129px 35px #F4846F',
'inner-header': '2px -20px 15px 30px rgba(244,132,111,1);',
},
fontFamily: {
Calibri : ["Calibri"]
} ,
colors: {
background: "var(--background)",
foreground: "var(--foreground)",

Loading…
Cancel
Save