Refacto context+styles and improved lightbox
|
@ -18,22 +18,24 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@fontsource/material-icons": "^4.5.4",
|
||||
"@fontsource/material-icons-outlined": "^4.5.4",
|
||||
"@fontsource/opendyslexic": "^4.5.4",
|
||||
"@fontsource/share-tech-mono": "^4.5.9",
|
||||
"@fontsource/vollkorn": "^4.5.12",
|
||||
"@fontsource/zen-maru-gothic": "^4.5.13",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"graphql-request": "^5.0.0",
|
||||
"markdown-to-jsx": "^7.1.7",
|
||||
"meilisearch": "^0.28.0",
|
||||
"next": "^12.3.1",
|
||||
"nodemailer": "^6.8.0",
|
||||
"npm": "^8.19.2",
|
||||
"rc-slider": "^10.0.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hot-keys": "^2.7.2",
|
||||
"react-hotkeys-hook": "^3.4.7",
|
||||
"react-swipeable": "^7.0.0",
|
||||
"react-zoom-pan-pinch": "^2.1.3",
|
||||
"string-natural-compare": "^3.0.1",
|
||||
|
@ -56,6 +58,7 @@
|
|||
"@types/string-natural-compare": "^3.0.2",
|
||||
"@types/throttle-debounce": "^5.0.0",
|
||||
"@types/turndown": "^5.0.1",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
||||
"@typescript-eslint/parser": "^5.40.1",
|
||||
"dotenv": "^16.0.3",
|
||||
|
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 8.4 KiB |
|
@ -1 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="creative-commons" class="svg-inline--fa fa-creative-commons" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M245.8 214.9l-33.22 17.28c-9.43-19.58-25.24-19.93-27.46-19.93-22.13 0-33.22 14.61-33.22 43.84 0 23.57 9.21 43.84 33.22 43.84 14.47 0 24.65-7.09 30.57-21.26l30.55 15.5c-6.17 11.51-25.69 38.98-65.1 38.98-22.6 0-73.96-10.32-73.96-77.05 0-58.69 43-77.06 72.63-77.06 30.72-.01 52.7 11.95 65.99 35.86zm143.1 0l-32.78 17.28c-9.5-19.77-25.72-19.93-27.9-19.93-22.14 0-33.22 14.61-33.22 43.84 0 23.55 9.23 43.84 33.22 43.84 14.45 0 24.65-7.09 30.54-21.26l31 15.5c-2.1 3.75-21.39 38.98-65.09 38.98-22.69 0-73.96-9.87-73.96-77.05 0-58.67 42.97-77.06 72.63-77.06 30.71-.01 52.58 11.95 65.56 35.86zM247.6 8.05C104.7 8.05 0 123.1 0 256c0 138.5 113.6 248 247.6 248 129.9 0 248.4-100.9 248.4-248 0-137.9-106.6-248-248.4-248zm.87 450.8c-112.5 0-203.7-93.04-203.7-202.8 0-105.4 85.43-203.3 203.7-203.3 112.5 0 202.8 89.46 202.8 203.3-.01 121.7-99.68 202.8-202.8 202.8z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M245.8 214.9l-33.22 17.28c-9.43-19.58-25.24-19.93-27.46-19.93-22.13 0-33.22 14.61-33.22 43.84 0 23.57 9.21 43.84 33.22 43.84 14.47 0 24.65-7.09 30.57-21.26l30.55 15.5c-6.17 11.51-25.69 38.98-65.1 38.98-22.6 0-73.96-10.32-73.96-77.05 0-58.69 43-77.06 72.63-77.06 30.72-.01 52.7 11.95 65.99 35.86zm143.1 0l-32.78 17.28c-9.5-19.77-25.72-19.93-27.9-19.93-22.14 0-33.22 14.61-33.22 43.84 0 23.55 9.23 43.84 33.22 43.84 14.45 0 24.65-7.09 30.54-21.26l31 15.5c-2.1 3.75-21.39 38.98-65.09 38.98-22.69 0-73.96-9.87-73.96-77.05 0-58.67 42.97-77.06 72.63-77.06 30.71-.01 52.58 11.95 65.56 35.86zM247.6 8.05C104.7 8.05 0 123.1 0 256c0 138.5 113.6 248 247.6 248C377.5 504 496 403.1 496 256 496 118.1 389.4 8 247.6 8zm.87 450.8c-112.5 0-203.7-93.04-203.7-202.8 0-105.4 85.43-203.3 203.7-203.3 112.5 0 202.8 89.46 202.8 203.3-.01 121.7-99.68 202.8-202.8 202.8z"/></svg>
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 925 B |
|
@ -1 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="creative-commons-by" class="svg-inline--fa fa-creative-commons-by" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M314.9 194.4v101.4h-28.3v120.5h-77.1V295.9h-28.3V194.4c0-4.4 1.6-8.2 4.6-11.3 3.1-3.1 6.9-4.7 11.3-4.7H299c4.1 0 7.8 1.6 11.1 4.7 3.1 3.2 4.8 6.9 4.8 11.3zm-101.5-63.7c0-23.3 11.5-35 34.5-35s34.5 11.7 34.5 35c0 23-11.5 34.5-34.5 34.5s-34.5-11.5-34.5-34.5zM247.6 8C389.4 8 496 118.1 496 256c0 147.1-118.5 248-248.4 248C113.6 504 0 394.5 0 256 0 123.1 104.7 8 247.6 8zm.8 44.7C130.2 52.7 44.7 150.6 44.7 256c0 109.8 91.2 202.8 203.7 202.8 103.2 0 202.8-81.1 202.8-202.8 .1-113.8-90.2-203.3-202.8-203.3z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M314.9 194.4v101.4h-28.3v120.5h-77.1V295.9h-28.3V194.4c0-4.4 1.6-8.2 4.6-11.3 3.1-3.1 6.9-4.7 11.3-4.7H299c4.1 0 7.8 1.6 11.1 4.7 3.1 3.2 4.8 6.9 4.8 11.3zm-101.5-63.7c0-23.3 11.5-35 34.5-35s34.5 11.7 34.5 35c0 23-11.5 34.5-34.5 34.5s-34.5-11.5-34.5-34.5zM247.6 8C389.4 8 496 118.1 496 256c0 147.1-118.5 248-248.4 248C113.6 504 0 394.5 0 256 0 123.1 104.7 8 247.6 8zm.8 44.7C130.2 52.7 44.7 150.6 44.7 256c0 109.8 91.2 202.8 203.7 202.8 103.2 0 202.8-81.1 202.8-202.8.1-113.8-90.2-203.3-202.8-203.3z"/></svg>
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 579 B |
|
@ -1 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="creative-commons-sa" class="svg-inline--fa fa-creative-commons-sa" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M247.6 8C389.4 8 496 118.1 496 256c0 147.1-118.5 248-248.4 248C113.6 504 0 394.5 0 256 0 123.1 104.7 8 247.6 8zm.8 44.7C130.2 52.7 44.7 150.6 44.7 256c0 109.8 91.2 202.8 203.7 202.8 103.2 0 202.8-81.1 202.8-202.8 .1-113.8-90.2-203.3-202.8-203.3zM137.7 221c13-83.9 80.5-95.7 108.9-95.7 99.8 0 127.5 82.5 127.5 134.2 0 63.6-41 132.9-128.9 132.9-38.9 0-99.1-20-109.4-97h62.5c1.5 30.1 19.6 45.2 54.5 45.2 23.3 0 58-18.2 58-82.8 0-82.5-49.1-80.6-56.7-80.6-33.1 0-51.7 14.6-55.8 43.8h18.2l-49.2 49.2-49-49.2h19.4z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M247.6 8C389.4 8 496 118.1 496 256c0 147.1-118.5 248-248.4 248C113.6 504 0 394.5 0 256 0 123.1 104.7 8 247.6 8zm.8 44.7C130.2 52.7 44.7 150.6 44.7 256c0 109.8 91.2 202.8 203.7 202.8 103.2 0 202.8-81.1 202.8-202.8.1-113.8-90.2-203.3-202.8-203.3zM137.7 221c13-83.9 80.5-95.7 108.9-95.7 99.8 0 127.5 82.5 127.5 134.2 0 63.6-41 132.9-128.9 132.9-38.9 0-99.1-20-109.4-97h62.5c1.5 30.1 19.6 45.2 54.5 45.2 23.3 0 58-18.2 58-82.8 0-82.5-49.1-80.6-56.7-80.6-33.1 0-51.7 14.6-55.8 43.8h18.2l-49.2 49.2-49-49.2h19.4z"/></svg>
|
Before Width: | Height: | Size: 757 B After Width: | Height: | Size: 586 B |
|
@ -1 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="discord" class="svg-inline--fa fa-discord" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M524.5 69.84a1.5 1.5 0 0 0 -.764-.7A485.1 485.1 0 0 0 404.1 32.03a1.816 1.816 0 0 0 -1.923 .91 337.5 337.5 0 0 0 -14.9 30.6 447.8 447.8 0 0 0 -134.4 0 309.5 309.5 0 0 0 -15.14-30.6 1.89 1.89 0 0 0 -1.924-.91A483.7 483.7 0 0 0 116.1 69.14a1.712 1.712 0 0 0 -.788 .676C39.07 183.7 18.19 294.7 28.43 404.4a2.016 2.016 0 0 0 .765 1.375A487.7 487.7 0 0 0 176 479.9a1.9 1.9 0 0 0 2.063-.676A348.2 348.2 0 0 0 208.1 430.4a1.86 1.86 0 0 0 -1.019-2.588 321.2 321.2 0 0 1 -45.87-21.85 1.885 1.885 0 0 1 -.185-3.126c3.082-2.309 6.166-4.711 9.109-7.137a1.819 1.819 0 0 1 1.9-.256c96.23 43.92 200.4 43.92 295.5 0a1.812 1.812 0 0 1 1.924 .233c2.944 2.426 6.027 4.851 9.132 7.16a1.884 1.884 0 0 1 -.162 3.126 301.4 301.4 0 0 1 -45.89 21.83 1.875 1.875 0 0 0 -1 2.611 391.1 391.1 0 0 0 30.01 48.81 1.864 1.864 0 0 0 2.063 .7A486 486 0 0 0 610.7 405.7a1.882 1.882 0 0 0 .765-1.352C623.7 277.6 590.9 167.5 524.5 69.84zM222.5 337.6c-28.97 0-52.84-26.59-52.84-59.24S193.1 219.1 222.5 219.1c29.67 0 53.31 26.82 52.84 59.24C275.3 310.1 251.9 337.6 222.5 337.6zm195.4 0c-28.97 0-52.84-26.59-52.84-59.24S388.4 219.1 417.9 219.1c29.67 0 53.31 26.82 52.84 59.24C470.7 310.1 447.5 337.6 417.9 337.6z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M524.5 69.84a1.5 1.5 0 00-.764-.7A485.1 485.1 0 00404.1 32.03a1.816 1.816 0 00-1.923.91 337.5 337.5 0 00-14.9 30.6 447.8 447.8 0 00-134.4 0 309.5 309.5 0 00-15.14-30.6 1.89 1.89 0 00-1.924-.91A483.7 483.7 0 00116.1 69.14a1.712 1.712 0 00-.788.676C39.07 183.7 18.19 294.7 28.43 404.4a2.016 2.016 0 00.765 1.375A487.7 487.7 0 00176 479.9a1.9 1.9 0 002.063-.676A348.2 348.2 0 00208.1 430.4a1.86 1.86 0 00-1.019-2.588 321.2 321.2 0 01-45.87-21.85 1.885 1.885 0 01-.185-3.126 251.047 251.047 0 009.109-7.137 1.819 1.819 0 011.9-.256c96.23 43.92 200.4 43.92 295.5 0a1.812 1.812 0 011.924.233 234.533 234.533 0 009.132 7.16 1.884 1.884 0 01-.162 3.126 301.4 301.4 0 01-45.89 21.83 1.875 1.875 0 00-1 2.611 391.1 391.1 0 0030.01 48.81 1.864 1.864 0 002.063.7A486 486 0 00610.7 405.7a1.882 1.882 0 00.765-1.352C623.7 277.6 590.9 167.5 524.5 69.84zm-302 267.76c-28.97 0-52.84-26.59-52.84-59.24s23.44-59.26 52.84-59.26c29.67 0 53.31 26.82 52.84 59.24-.04 31.76-23.44 59.26-52.84 59.26zm195.4 0c-28.97 0-52.84-26.59-52.84-59.24s23.34-59.26 52.84-59.26c29.67 0 53.31 26.82 52.84 59.24-.04 31.76-23.24 59.26-52.84 59.26z"/></svg>
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -1 +1 @@
|
|||
<svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="svg-inline--fa fa-github" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"/></svg>
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 871 B |
|
@ -1,29 +1,15 @@
|
|||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import UAParser from "ua-parser-js";
|
||||
import { useIsClient } from "usehooks-ts";
|
||||
import Script from "next/script";
|
||||
import { layout } from "../../design.config";
|
||||
import { Ico, Icon } from "./Ico";
|
||||
import { ButtonGroup } from "./Inputs/ButtonGroup";
|
||||
import { OrderableList } from "./Inputs/OrderableList";
|
||||
import { Select } from "./Inputs/Select";
|
||||
import { TextInput } from "./Inputs/TextInput";
|
||||
import { MainPanel } from "./Panels/MainPanel";
|
||||
import { Popup } from "./Popup";
|
||||
import { filterHasAttributes, isDefined, isUndefined } from "helpers/others";
|
||||
import { prettyLanguage } from "helpers/formatters";
|
||||
import { SafariPopup } from "./Panels/SafariPopup";
|
||||
import { isDefined, isUndefined } from "helpers/others";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph";
|
||||
import { useIs1ColumnLayout, useIsScreenAtLeast } from "hooks/useContainerQuery";
|
||||
import { useOnResize } from "hooks/useOnResize";
|
||||
import { Ids } from "types/ids";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
|
@ -60,48 +46,19 @@ export const AppLayout = ({
|
|||
contentPanelScroolbar = true,
|
||||
}: Props): JSX.Element => {
|
||||
const {
|
||||
configPanelOpen,
|
||||
mainPanelOpen,
|
||||
mainPanelReduced,
|
||||
menuGestures,
|
||||
subPanelOpen,
|
||||
hasDisgardedSafariWarning,
|
||||
setHasDisgardedSafariWarning,
|
||||
setConfigPanelOpen,
|
||||
setMainPanelOpen,
|
||||
setSubPanelOpen,
|
||||
toggleMainPanelOpen,
|
||||
toggleSubPanelOpen,
|
||||
} = useAppLayout();
|
||||
|
||||
const { setScreenWidth, setContentPanelWidth, setSubPanelWidth } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
|
||||
const { langui, currencies, languages } = useLocalData();
|
||||
|
||||
const {
|
||||
currency,
|
||||
darkMode,
|
||||
dyslexic,
|
||||
fontSize,
|
||||
playerName,
|
||||
preferredLanguages,
|
||||
selectedThemeMode,
|
||||
setCurrency,
|
||||
setDarkMode,
|
||||
setDyslexic,
|
||||
setFontSize,
|
||||
setPlayerName,
|
||||
setPreferredLanguages,
|
||||
setSelectedThemeMode,
|
||||
} = useUserSettings();
|
||||
|
||||
const router = useRouter();
|
||||
const is1ColumnLayout = useIs1ColumnLayout();
|
||||
const isScreenAtLeastXs = useIsScreenAtLeast("xs");
|
||||
|
||||
useOnResize(Ids.Body, (width) => setScreenWidth(width));
|
||||
useOnResize(Ids.ContentPanel, (width) => setContentPanelWidth(width));
|
||||
useOnResize(Ids.SubPanel, (width) => setSubPanelWidth(width));
|
||||
const { is1ColumnLayout, isScreenAtLeastXs } = useContainerQueries();
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: (SwipeEventData) => {
|
||||
|
@ -131,47 +88,13 @@ export const AppLayout = ({
|
|||
[contentPanel, subPanel]
|
||||
);
|
||||
|
||||
const currencyOptions = useMemo(
|
||||
() =>
|
||||
filterHasAttributes(currencies, ["attributes"] as const).map(
|
||||
(currentCurrency) => currentCurrency.attributes.code
|
||||
),
|
||||
[currencies]
|
||||
);
|
||||
|
||||
const [currencySelect, setCurrencySelect] = useState<number>(-1);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(currency)) setCurrencySelect(currencyOptions.indexOf(currency));
|
||||
}, [currency, currencyOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currencySelect >= 0) setCurrency(currencyOptions[currencySelect]);
|
||||
}, [currencyOptions, currencySelect, setCurrency]);
|
||||
|
||||
const isClient = useIsClient();
|
||||
|
||||
const isSafari = useMemo<boolean>(() => {
|
||||
if (isClient) {
|
||||
const parser = new UAParser();
|
||||
return parser.getBrowser().name === "Safari" || parser.getOS().name === "iOS";
|
||||
}
|
||||
return false;
|
||||
}, [isClient]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cJoin(
|
||||
cIf(darkMode, "set-theme-dark", "set-theme-light"),
|
||||
cIf(dyslexic, "set-theme-font-dyslexic", "set-theme-font-standard")
|
||||
)}>
|
||||
<div
|
||||
{...handlers}
|
||||
id={Ids.Body}
|
||||
className={cJoin(
|
||||
`fixed inset-0 m-0 grid touch-pan-y bg-light p-0 text-black
|
||||
[grid-template-areas:'main_sub_content']`,
|
||||
cIf(is1ColumnLayout, `grid-rows-[1fr_5rem] [grid-template-areas:'content''navbar']`)
|
||||
"fixed inset-0 m-0 grid touch-pan-y bg-light p-0 [grid-template-areas:'main_sub_content']",
|
||||
cIf(is1ColumnLayout, "grid-rows-[1fr_5rem] [grid-template-areas:'content''navbar']")
|
||||
)}
|
||||
style={{
|
||||
gridTemplateColumns: is1ColumnLayout
|
||||
|
@ -199,21 +122,14 @@ export const AppLayout = ({
|
|||
<meta property="og:image:type" content="image/jpeg" />
|
||||
</Head>
|
||||
|
||||
<Script
|
||||
async
|
||||
defer
|
||||
data-website-id={process.env.NEXT_PUBLIC_UMAMI_ID}
|
||||
src={`${process.env.NEXT_PUBLIC_UMAMI_URL}/umami.js`}
|
||||
/>
|
||||
|
||||
{/* Background when navbar is opened */}
|
||||
<div
|
||||
className={cJoin(
|
||||
`absolute inset-0 transition-[backdrop-filter] duration-500
|
||||
`absolute inset-0 transition-filter duration-500
|
||||
[grid-area:content]`,
|
||||
cIf(
|
||||
(mainPanelOpen || subPanelOpen) && is1ColumnLayout,
|
||||
"z-10 [backdrop-filter:blur(2px)]",
|
||||
"z-10 backdrop-blur",
|
||||
"pointer-events-none touch-none"
|
||||
)
|
||||
)}>
|
||||
|
@ -250,7 +166,7 @@ export const AppLayout = ({
|
|||
<div
|
||||
id={Ids.SubPanel}
|
||||
className={cJoin(
|
||||
`texture-paper-dots z-20 overflow-y-scroll border-r-[1px] border-dark/50
|
||||
`texture-paper-dots z-20 overflow-y-scroll border-r border-dark/50
|
||||
bg-light transition-transform duration-300 [scrollbar-width:none]
|
||||
webkit-scrollbar:w-0`,
|
||||
cIf(
|
||||
|
@ -258,7 +174,7 @@ export const AppLayout = ({
|
|||
"justify-self-end border-r-0 [grid-area:content]",
|
||||
"[grid-area:sub]"
|
||||
),
|
||||
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)] border-l-[1px]"),
|
||||
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)] border-l"),
|
||||
cIf(is1ColumnLayout && !subPanelOpen && !turnSubIntoContent, "translate-x-[100vw]"),
|
||||
cIf(is1ColumnLayout && turnSubIntoContent, "w-full border-l-0")
|
||||
)}>
|
||||
|
@ -269,7 +185,7 @@ export const AppLayout = ({
|
|||
{/* Main panel */}
|
||||
<div
|
||||
className={cJoin(
|
||||
`texture-paper-dots z-30 overflow-y-scroll border-r-[1px] border-dark/50
|
||||
`texture-paper-dots z-30 overflow-y-scroll border-r border-dark/50
|
||||
bg-light transition-transform duration-300 [scrollbar-width:none] webkit-scrollbar:w-0`,
|
||||
cIf(is1ColumnLayout, "justify-self-start [grid-area:content]", "[grid-area:main]"),
|
||||
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)]"),
|
||||
|
@ -282,7 +198,7 @@ export const AppLayout = ({
|
|||
<div
|
||||
className={cJoin(
|
||||
`texture-paper-dots z-10 grid grid-cols-[5rem_1fr_5rem] place-items-center
|
||||
border-t-[1px] border-dotted border-black bg-light [grid-area:navbar]`,
|
||||
border-t border-dotted border-black bg-light [grid-area:navbar]`,
|
||||
cIf(!is1ColumnLayout, "hidden")
|
||||
)}>
|
||||
<Ico
|
||||
|
@ -313,207 +229,7 @@ export const AppLayout = ({
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Popup state={isSafari && !hasDisgardedSafariWarning} onClose={() => null}>
|
||||
<h1 className="text-2xl">Hi, you are using Safari!</h1>
|
||||
<p className="max-w-lg text-center">
|
||||
In most cases this wouldn’t be a problem but our website is—for some obscure
|
||||
reason—performing terribly on Safari (WebKit). Because of that, we have decided to
|
||||
display this message instead of letting you have a slow and painful experience. We are
|
||||
looking into the problem, and are hoping to fix this soon.
|
||||
</p>
|
||||
<p>In the meanwhile, if you are using an iPhone/iPad, please try using another device.</p>
|
||||
<p>If you are on macOS, please use another browser such as Firefox or Chrome.</p>
|
||||
|
||||
<Button
|
||||
text="Let me in regardless"
|
||||
className="mt-8"
|
||||
onClick={() => {
|
||||
setHasDisgardedSafariWarning(true);
|
||||
sendAnalytics("Safari", "Disgard warning");
|
||||
}}
|
||||
/>
|
||||
</Popup>
|
||||
|
||||
<Popup
|
||||
state={configPanelOpen}
|
||||
onClose={() => {
|
||||
setConfigPanelOpen(false);
|
||||
sendAnalytics("Settings", "Close settings");
|
||||
}}>
|
||||
<h2 className="text-2xl">{langui.settings}</h2>
|
||||
|
||||
<div
|
||||
className={cJoin(
|
||||
`mt-4 grid justify-items-center gap-16 text-center`,
|
||||
cIf(!is1ColumnLayout, "grid-cols-[auto_auto]")
|
||||
)}>
|
||||
{router.locales && (
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.languages}</h3>
|
||||
{preferredLanguages.length > 0 && (
|
||||
<OrderableList
|
||||
items={preferredLanguages.map((locale) => ({
|
||||
code: locale,
|
||||
name: prettyLanguage(locale, languages),
|
||||
}))}
|
||||
insertLabels={[
|
||||
{
|
||||
insertAt: 0,
|
||||
name: langui.primary_language ?? "Primary language",
|
||||
},
|
||||
{
|
||||
insertAt: 1,
|
||||
name: langui.secondary_language ?? "Secondary languages",
|
||||
},
|
||||
]}
|
||||
onChange={(items) => {
|
||||
const newPreferredLanguages = items.map((item) => item.code);
|
||||
setPreferredLanguages(newPreferredLanguages);
|
||||
sendAnalytics("Settings", "Change preferred languages");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={cJoin(
|
||||
"grid place-items-center gap-8 text-center",
|
||||
cIf(!is1ColumnLayout, "grid-cols-2")
|
||||
)}>
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.theme}</h3>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{
|
||||
onClick: () => {
|
||||
setDarkMode(false);
|
||||
setSelectedThemeMode(true);
|
||||
sendAnalytics("Settings", "Change theme (light)");
|
||||
},
|
||||
active: selectedThemeMode && !darkMode,
|
||||
text: langui.light,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setSelectedThemeMode(false);
|
||||
sendAnalytics("Settings", "Change theme (auto)");
|
||||
},
|
||||
active: !selectedThemeMode,
|
||||
text: langui.auto,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setDarkMode(true);
|
||||
setSelectedThemeMode(true);
|
||||
sendAnalytics("Settings", "Change theme (dark)");
|
||||
},
|
||||
active: selectedThemeMode && darkMode,
|
||||
text: langui.dark,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.currency}</h3>
|
||||
<div>
|
||||
<Select
|
||||
options={currencyOptions}
|
||||
value={currencySelect}
|
||||
onChange={(newCurrency) => {
|
||||
setCurrencySelect(newCurrency);
|
||||
sendAnalytics(
|
||||
"Settings",
|
||||
`Change currency (${currencyOptions[newCurrency]})}`
|
||||
);
|
||||
}}
|
||||
className="w-28"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.font_size}</h3>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{
|
||||
onClick: () => {
|
||||
setFontSize((current) => current / 1.05);
|
||||
sendAnalytics(
|
||||
"Settings",
|
||||
`Change font size (${((fontSize / 1.05) * 100).toLocaleString(undefined, {
|
||||
maximumFractionDigits: 0,
|
||||
})}%)`
|
||||
);
|
||||
},
|
||||
icon: Icon.TextDecrease,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setFontSize(1);
|
||||
sendAnalytics("Settings", "Change font size (100%)");
|
||||
},
|
||||
text: `${(fontSize * 100).toLocaleString(undefined, {
|
||||
maximumFractionDigits: 0,
|
||||
})}%`,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setFontSize((current) => current * 1.05);
|
||||
sendAnalytics(
|
||||
"Settings",
|
||||
`Change font size (${(fontSize * 1.05 * 100).toLocaleString(undefined, {
|
||||
maximumFractionDigits: 0,
|
||||
})}%)`
|
||||
);
|
||||
},
|
||||
icon: Icon.TextIncrease,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.font}</h3>
|
||||
<div className="grid gap-2">
|
||||
<Button
|
||||
active={!dyslexic}
|
||||
onClick={() => {
|
||||
setDyslexic(false);
|
||||
sendAnalytics("Settings", "Change font (Zen Maru Gothic)");
|
||||
}}
|
||||
className="font-zenMaruGothic"
|
||||
text="Zen Maru Gothic"
|
||||
/>
|
||||
<Button
|
||||
active={dyslexic}
|
||||
onClick={() => {
|
||||
setDyslexic(true);
|
||||
sendAnalytics("Settings", "Change font (OpenDyslexic)");
|
||||
}}
|
||||
className="font-openDyslexic"
|
||||
text="OpenDyslexic"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.player_name}</h3>
|
||||
<TextInput
|
||||
placeholder="<player>"
|
||||
className="w-48"
|
||||
value={playerName}
|
||||
onChange={(newName) => {
|
||||
setPlayerName(newName);
|
||||
sendAnalytics("Settings", "Change username");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</div>
|
||||
<SafariPopup />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,8 +15,8 @@ interface Props {
|
|||
export const Chip = ({ className, text }: Props): JSX.Element => (
|
||||
<div
|
||||
className={cJoin(
|
||||
`grid place-content-center place-items-center whitespace-nowrap rounded-full border-[1px]
|
||||
px-1.5 pb-[0.14rem] text-xs opacity-70 transition-[color,_opacity,_border-color]
|
||||
`grid place-content-center place-items-center whitespace-nowrap rounded-full border
|
||||
px-1.5 pb-[0.14rem] text-xs opacity-70 transition-[color,opacity,border-color]
|
||||
hover:opacity-100`,
|
||||
className
|
||||
)}>
|
||||
|
|
|
@ -22,10 +22,10 @@ const ChroniclePreview = ({ date, url, title, isActive }: Props): JSX.Element =>
|
|||
href={url}
|
||||
className={cJoin(
|
||||
`flex w-full cursor-pointer gap-4 rounded-2xl py-4 px-5 text-left align-top outline outline-2
|
||||
outline-offset-[-2px] outline-mid transition-all hover:bg-mid hover:shadow-inner-sm
|
||||
hover:shadow-shade hover:outline-[transparent] hover:active:shadow-inner
|
||||
-outline-offset-2 outline-mid transition-all hover:bg-mid hover:shadow-inner-sm
|
||||
hover:shadow-shade hover:outline-transparent hover:active:shadow-inner
|
||||
hover:active:shadow-shade`,
|
||||
cIf(isActive, "bg-mid shadow-inner-sm shadow-shade outline-[transparent]")
|
||||
cIf(isActive, "bg-mid shadow-inner-sm shadow-shade outline-transparent")
|
||||
)}>
|
||||
<div className="text-right">
|
||||
<p>{date.year}</p>
|
||||
|
|
|
@ -38,7 +38,7 @@ const ChroniclesList = ({ chronicles, currentSlug, title }: Props): JSX.Element
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="grid gap-4 overflow-hidden transition-[max-height] duration-500"
|
||||
className="grid gap-4 overflow-hidden transition-height duration-500"
|
||||
style={{ maxHeight: isOpen ? `${8 * chronicles.length}rem` : 0 }}>
|
||||
{filterHasAttributes(chronicles, [
|
||||
"attributes.contents",
|
||||
|
|
|
@ -234,7 +234,7 @@ export const Terminal = ({
|
|||
return (
|
||||
<div
|
||||
className={cJoin(
|
||||
"h-screen overflow-hidden bg-light text-black set-theme-font-standard",
|
||||
"h-screen overflow-hidden bg-light set-theme-font-standard",
|
||||
cIf(darkMode, "set-theme-dark", "set-theme-light")
|
||||
)}>
|
||||
<div
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { cJoin } from "helpers/className";
|
||||
|
||||
interface Props {
|
||||
src: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ColoredSvg = ({ src, className }: Props): JSX.Element => (
|
||||
<div
|
||||
className={cJoin(
|
||||
`transition-colors ![mask-size:contain] ![mask-repeat:no-repeat] ![mask-position:center]`,
|
||||
className
|
||||
)}
|
||||
style={{ mask: `url('${src}')`, WebkitMask: `url('${src}')` }}
|
||||
/>
|
||||
);
|
|
@ -12,6 +12,5 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const HorizontalLine = ({ className }: Props): JSX.Element => (
|
||||
<div
|
||||
className={cJoin("my-8 h-0 w-full border-t-[3px] border-dotted border-black", className)}></div>
|
||||
<div className={cJoin("my-8 h-0 w-full border-t-2 border-dotted border-black", className)}></div>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { MouseEventHandler } from "react";
|
||||
import { cJoin } from "helpers/className";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -10,14 +10,19 @@ interface Props {
|
|||
className?: string;
|
||||
onClick?: MouseEventHandler<HTMLSpanElement> | undefined;
|
||||
icon: Icon;
|
||||
isFilled?: boolean;
|
||||
}
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Ico = ({ onClick, icon, className }: Props): JSX.Element => (
|
||||
export const Ico = ({ onClick, icon, className, isFilled = true }: Props): JSX.Element => (
|
||||
<span
|
||||
onClick={onClick}
|
||||
className={cJoin("material-icons [font-size:inherit] [line-height:inherit]", className)}>
|
||||
className={cJoin(
|
||||
"[font-size:inherit] [line-height:inherit]",
|
||||
cIf(isFilled, "material-icons", "material-icons-outlined"),
|
||||
className
|
||||
)}>
|
||||
{icon}
|
||||
</span>
|
||||
);
|
||||
|
|
|
@ -58,7 +58,7 @@ export const Button = ({
|
|||
onFocus={(event) => event.target.blur()}
|
||||
className={cJoin(
|
||||
`group grid cursor-pointer select-none grid-flow-col place-content-center
|
||||
place-items-center gap-2 rounded-full border-[1px] border-dark py-3 px-4
|
||||
place-items-center gap-2 rounded-full border border-dark py-3 px-4
|
||||
leading-none text-dark transition-all`,
|
||||
cIf(
|
||||
active,
|
||||
|
|
|
@ -57,7 +57,7 @@ export const OrderableList = ({ onChange, items, insertLabels }: Props): JSX.Ele
|
|||
updateOrder(sourceIndex, targetIndex);
|
||||
}}
|
||||
className="grid cursor-grab select-none grid-cols-[auto_1fr] place-content-center gap-2
|
||||
rounded-full border-[1px] border-dark bg-light px-1 py-2 pr-4 text-dark transition-all
|
||||
rounded-full border border-dark bg-light px-1 py-2 pr-4 text-dark transition-all
|
||||
hover:bg-dark hover:text-light hover:drop-shadow-shade-lg"
|
||||
draggable>
|
||||
<div className="grid grid-rows-[.8em_.8em] place-items-center">
|
||||
|
|
|
@ -34,16 +34,16 @@ export const Select = ({ className, value, options, allowEmpty, onChange }: Prop
|
|||
<div
|
||||
ref={ref}
|
||||
className={cJoin(
|
||||
"relative text-center transition-[filter]",
|
||||
"relative text-center transition-filter",
|
||||
cIf(isOpened, "z-10 drop-shadow-shade-lg"),
|
||||
className
|
||||
)}>
|
||||
<div
|
||||
className={cJoin(
|
||||
`grid cursor-pointer grid-flow-col grid-cols-[1fr_auto_auto] place-items-center
|
||||
rounded-[1em] bg-light p-1 outline outline-2 outline-offset-[-2px] outline-mid
|
||||
transition-all hover:bg-mid hover:outline-[transparent]`,
|
||||
cIf(isOpened, "rounded-b-none bg-highlight outline-[transparent]")
|
||||
rounded-[1em] bg-light p-1 outline outline-1 -outline-offset-1 outline-mid
|
||||
transition-all hover:bg-mid hover:outline-transparent`,
|
||||
cIf(isOpened, "rounded-b-none bg-highlight outline-transparent")
|
||||
)}>
|
||||
<p onClick={tryToggling} className="w-full">
|
||||
{value === -1 ? "—" : options[value]}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useState } from "react";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
|
@ -14,21 +15,35 @@ interface Props {
|
|||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Switch = ({ value, onClick, className, disabled = false }: Props): JSX.Element => (
|
||||
export const Switch = ({ value, onClick, className, disabled = false }: Props): JSX.Element => {
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
return (
|
||||
<div
|
||||
className={cJoin(
|
||||
"relative grid h-6 w-12 rounded-full border-2 border-mid transition-colors",
|
||||
`relative grid h-6 w-12 content-center rounded-full border-mid outline
|
||||
outline-1 -outline-offset-1 outline-mid transition-colors`,
|
||||
cIf(disabled, "cursor-not-allowed", "cursor-pointer"),
|
||||
cIf(value, "border-none bg-mid shadow-inner-sm shadow-shade", "bg-light"),
|
||||
cIf(
|
||||
value,
|
||||
"border-none bg-mid shadow-inner-sm shadow-shade outline-transparent",
|
||||
"bg-light"
|
||||
),
|
||||
className
|
||||
)}
|
||||
onClick={() => {
|
||||
if (!disabled) onClick();
|
||||
}}>
|
||||
}}
|
||||
onPointerDown={() => setIsFocused(true)}
|
||||
onPointerOut={() => setIsFocused(false)}
|
||||
onPointerLeave={() => setIsFocused(false)}
|
||||
onPointerUp={() => setIsFocused(false)}>
|
||||
<div
|
||||
className={cJoin(
|
||||
"absolute aspect-square rounded-full bg-dark transition-transform",
|
||||
cIf(value, "top-[2px] bottom-[2px] left-[2px] translate-x-[120%]", "top-0 bottom-0 left-0")
|
||||
)}></div>
|
||||
"ml-1 h-4 w-4 rounded-full bg-dark transition-transform",
|
||||
cIf(value, "translate-x-6"),
|
||||
cIf(isFocused, cIf(value, "translate-x-5", "translate-x-1"))
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,95 +1,136 @@
|
|||
import { Dispatch, SetStateAction, useCallback } from "react";
|
||||
import Hotkeys from "react-hot-keys";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
|
||||
import { useState } from "react";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import { Img } from "./Img";
|
||||
import { Button } from "./Inputs/Button";
|
||||
import { Popup } from "./Popup";
|
||||
import { Icon } from "components/Ico";
|
||||
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ────────────────────────────────────────╯ CONSTANTS ╰──────────────────────────────────────────
|
||||
*/
|
||||
|
||||
const SENSIBILITY_SWIPE = 0.5;
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
||||
*/
|
||||
import { useFullscreen } from "hooks/useFullscreen";
|
||||
import { Ids } from "types/ids";
|
||||
import { UploadImageFragment } from "graphql/generated";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { isDefined } from "helpers/others";
|
||||
|
||||
interface Props {
|
||||
setState: Dispatch<SetStateAction<boolean | undefined>> | Dispatch<SetStateAction<boolean>>;
|
||||
state: boolean;
|
||||
images: string[];
|
||||
index: number;
|
||||
setIndex: Dispatch<SetStateAction<number>>;
|
||||
onCloseRequest: () => void;
|
||||
isVisible: boolean;
|
||||
image?: UploadImageFragment | string;
|
||||
isNextImageAvailable?: boolean;
|
||||
isPreviousImageAvailable?: boolean;
|
||||
onPressNext?: () => void;
|
||||
onPressPrevious?: () => void;
|
||||
}
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const LightBox = ({ state, setState, images, index, setIndex }: Props): JSX.Element => {
|
||||
const handlePrevious = useCallback(() => {
|
||||
if (index > 0) setIndex(index - 1);
|
||||
}, [index, setIndex]);
|
||||
const is3ColumnsLayout = useIs3ColumnsLayout();
|
||||
export const LightBox = ({
|
||||
onCloseRequest,
|
||||
isVisible,
|
||||
image: src,
|
||||
isPreviousImageAvailable = false,
|
||||
onPressPrevious,
|
||||
isNextImageAvailable = false,
|
||||
onPressNext,
|
||||
}: Props): JSX.Element => {
|
||||
const [currentZoom, setCurrentZoom] = useState(1);
|
||||
const { isFullscreen, toggleFullscreen, exitFullscreen, requestFullscreen } = useFullscreen(
|
||||
Ids.LightBox
|
||||
);
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
if (index < images.length - 1) setIndex(index + 1);
|
||||
}, [images.length, index, setIndex]);
|
||||
useHotkeys(
|
||||
"left",
|
||||
() => onPressPrevious?.(),
|
||||
{ enabled: isVisible && isPreviousImageAvailable },
|
||||
[onPressPrevious]
|
||||
);
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: (SwipeEventData) => {
|
||||
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
||||
handleNext();
|
||||
},
|
||||
onSwipedRight: (SwipeEventData) => {
|
||||
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
||||
handlePrevious();
|
||||
},
|
||||
});
|
||||
useHotkeys("f", () => requestFullscreen(), { enabled: isVisible && !isFullscreen }, [
|
||||
requestFullscreen,
|
||||
]);
|
||||
|
||||
useHotkeys("right", () => onPressNext?.(), { enabled: isVisible && isNextImageAvailable }, [
|
||||
onPressNext,
|
||||
]);
|
||||
|
||||
useHotkeys("escape", onCloseRequest, { enabled: isVisible }, [onCloseRequest]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{state && (
|
||||
<Hotkeys
|
||||
keyName="left,right"
|
||||
allowRepeat
|
||||
onKeyDown={(keyName) => {
|
||||
if (keyName === "left") {
|
||||
handlePrevious();
|
||||
} else {
|
||||
handleNext();
|
||||
}
|
||||
}}>
|
||||
<Popup onClose={() => setState(false)} state={state} padding={false} fillViewport>
|
||||
<div
|
||||
{...handlers}
|
||||
id={Ids.LightBox}
|
||||
className={cJoin(
|
||||
`grid h-full w-full place-items-center overflow-hidden first-letter:gap-4`,
|
||||
cIf(
|
||||
is3ColumnsLayout,
|
||||
`grid-cols-[4em,1fr,4em] [grid-template-areas:"left_image_right"]`,
|
||||
`grid-cols-2 [grid-template-areas:"image_image""left_right"]`
|
||||
)
|
||||
"fixed inset-0 z-50 grid place-content-center transition-filter duration-500",
|
||||
cIf(isVisible, "backdrop-blur", "pointer-events-none touch-none")
|
||||
)}>
|
||||
<div className="ml-4 [grid-area:left]">
|
||||
{index > 0 && <Button onClick={handlePrevious} icon={Icon.ChevronLeft} />}
|
||||
</div>
|
||||
|
||||
<Img className="max-h-full min-h-fit [grid-area:image]" src={images[index]} />
|
||||
|
||||
<div className="mr-4 [grid-area:right]">
|
||||
{index < images.length - 1 && (
|
||||
<Button onClick={handleNext} icon={Icon.ChevronRight} />
|
||||
<div
|
||||
className={cJoin(
|
||||
"fixed inset-0 bg-shade transition-all duration-500",
|
||||
cIf(isVisible, "bg-opacity-50", "bg-opacity-0")
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
</Hotkeys>
|
||||
/>
|
||||
<div
|
||||
className={cJoin(
|
||||
"absolute inset-8 grid transition-transform",
|
||||
cIf(isVisible, "scale-100", "scale-0")
|
||||
)}>
|
||||
<TransformWrapper
|
||||
onZoom={(zoom) => setCurrentZoom(zoom.state.scale)}
|
||||
panning={{ disabled: currentZoom <= 1, velocityDisabled: false }}
|
||||
doubleClick={{ disabled: true, mode: "reset" }}
|
||||
zoomAnimation={{ size: 0.1 }}
|
||||
velocityAnimation={{ animationTime: 0, equalToMove: true }}>
|
||||
{({ resetTransform }) => (
|
||||
<>
|
||||
<TransformComponent
|
||||
wrapperStyle={{
|
||||
overflow: "visible",
|
||||
placeSelf: "center",
|
||||
}}>
|
||||
{isDefined(src) && (
|
||||
<Img
|
||||
className={`drop-shadow-shade-2xl-shade h-[calc(100vh-4rem)] w-full
|
||||
object-contain`}
|
||||
src={src}
|
||||
quality={ImageQuality.Large}
|
||||
/>
|
||||
)}
|
||||
</TransformComponent>
|
||||
|
||||
{isPreviousImageAvailable && (
|
||||
<div
|
||||
className={`absolute top-1/2 left-0 grid gap-4 rounded-[2rem] p-4
|
||||
backdrop-blur-lg`}>
|
||||
<Button icon={Icon.NavigateBefore} onClick={onPressPrevious} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isNextImageAvailable && (
|
||||
<div
|
||||
className={`absolute top-1/2 right-0 grid gap-4 rounded-[2rem] p-4
|
||||
backdrop-blur-lg`}>
|
||||
<Button icon={Icon.NavigateNext} onClick={onPressNext} />{" "}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={`absolute top-0 right-0 grid gap-4 rounded-[2rem] p-4
|
||||
backdrop-blur-lg`}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
resetTransform();
|
||||
exitFullscreen();
|
||||
onCloseRequest();
|
||||
}}
|
||||
icon={Icon.Close}
|
||||
/>
|
||||
<Button
|
||||
icon={isFullscreen ? Icon.FullscreenExit : Icon.Fullscreen}
|
||||
onClick={toggleFullscreen}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</TransformWrapper>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,14 +9,14 @@ import { cIf, cJoin } from "helpers/className";
|
|||
import { slugify } from "helpers/formatters";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { isDefined, isDefinedAndNotEmpty, isUndefined } from "helpers/others";
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import { AnchorShare } from "components/AnchorShare";
|
||||
import { useIntersectionList } from "hooks/useIntersectionList";
|
||||
import { Ico, Icon } from "components/Ico";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { useLightBox } from "contexts/LightBoxContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -33,8 +33,8 @@ interface MarkdawnProps {
|
|||
export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Element => {
|
||||
const { playerName } = useUserSettings();
|
||||
const router = useRouter();
|
||||
const isContentPanelAtLeastLg = useIsContentPanelAtLeast("lg");
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
const { isContentPanelAtLeastLg } = useContainerQueries();
|
||||
const { showLightBox } = useLightBox();
|
||||
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
const text = useMemo(
|
||||
|
@ -49,8 +49,6 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<LightBox />
|
||||
<Markdown
|
||||
className={cJoin("formatted", className)}
|
||||
options={{
|
||||
|
@ -125,10 +123,7 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
|
|||
component: (compProps) => (
|
||||
<>
|
||||
<strong
|
||||
className={cJoin(
|
||||
"!my-0 text-dark/60",
|
||||
cIf(!isContentPanelAtLeastLg, "!-mb-4")
|
||||
)}>
|
||||
className={cJoin("!my-0 text-dark/60", cIf(!isContentPanelAtLeastLg, "!-mb-4"))}>
|
||||
<Markdawn text={compProps.name} />
|
||||
</strong>
|
||||
<p className="whitespace-pre-line">{compProps.children}</p>
|
||||
|
@ -196,7 +191,7 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
|
|||
<div
|
||||
className="mt-8 mb-12 grid cursor-pointer place-content-center"
|
||||
onClick={() => {
|
||||
openLightBox([
|
||||
showLightBox([
|
||||
compProps.src.startsWith("/uploads/")
|
||||
? getAssetURL(compProps.src, ImageQuality.Large)
|
||||
: compProps.src,
|
||||
|
@ -217,7 +212,6 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
|
|||
}}>
|
||||
{text}
|
||||
</Markdown>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -61,13 +61,10 @@ export const NavOption = ({
|
|||
justify-center gap-x-5 rounded-2xl p-4 transition-all hover:bg-mid hover:shadow-inner-sm
|
||||
hover:shadow-shade hover:active:shadow-inner hover:active:shadow-shade`,
|
||||
cIf(icon, "text-left", "text-center"),
|
||||
cIf(
|
||||
border,
|
||||
"outline outline-2 outline-offset-[-2px] outline-mid hover:outline-[transparent]"
|
||||
),
|
||||
cIf(border, "outline outline-2 -outline-offset-2 outline-mid hover:outline-transparent"),
|
||||
cIf(isActive, "bg-mid shadow-inner-sm shadow-shade")
|
||||
)}>
|
||||
{icon && <Ico icon={icon} className="mt-[-.1em] !text-2xl" />}
|
||||
{icon && <Ico icon={icon} className="mt-[-.1em] !text-2xl" isFilled={isActive} />}
|
||||
|
||||
{!reduced && (
|
||||
<div>
|
||||
|
|
|
@ -4,9 +4,9 @@ import { Button } from "components/Inputs/Button";
|
|||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -26,7 +26,7 @@ interface Props {
|
|||
export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props): JSX.Element => {
|
||||
const { setSubPanelOpen } = useAppLayout();
|
||||
const { langui } = useLocalData();
|
||||
const is3ColumnsLayout = useIs3ColumnsLayout();
|
||||
const { is3ColumnsLayout } = useContainerQueries();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -25,7 +25,7 @@ export const ContentPanel = ({
|
|||
children,
|
||||
className,
|
||||
}: Props): JSX.Element => {
|
||||
const isContentPanelAtLeast3xl = useIsContentPanelAtLeast("3xl");
|
||||
const { isContentPanelAtLeast3xl } = useContainerQueries();
|
||||
return (
|
||||
<div className="grid h-full">
|
||||
<main
|
||||
|
|
|
@ -8,9 +8,10 @@ import { Icon } from "components/Ico";
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { Link } from "components/Inputs/Link";
|
||||
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { ColoredSvg } from "components/ColoredSvg";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -18,7 +19,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
*/
|
||||
|
||||
export const MainPanel = (): JSX.Element => {
|
||||
const is3ColumnsLayout = useIs3ColumnsLayout();
|
||||
const { is3ColumnsLayout } = useContainerQueries();
|
||||
const { mainPanelReduced, toggleMainPanelReduced, setConfigPanelOpen } = useAppLayout();
|
||||
const { langui } = useLocalData();
|
||||
|
||||
|
@ -51,14 +52,14 @@ export const MainPanel = (): JSX.Element => {
|
|||
)}
|
||||
<div>
|
||||
<div className="grid place-items-center">
|
||||
<Link href="/" className="flex w-full justify-center">
|
||||
<div
|
||||
<Link href="/" className="flex w-full cursor-pointer justify-center">
|
||||
<ColoredSvg
|
||||
src="/icons/accords.svg"
|
||||
className={cJoin(
|
||||
`mb-4 aspect-square cursor-pointer bg-black transition-colors
|
||||
[mask:url('/icons/accords.svg')] ![mask-size:contain] ![mask-repeat:no-repeat]
|
||||
![mask-position:center] hover:bg-dark`,
|
||||
"mb-4 aspect-square bg-black hover:bg-dark",
|
||||
cIf(mainPanelReduced && is3ColumnsLayout, "w-12", "w-1/2")
|
||||
)}></div>
|
||||
)}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
{(!mainPanelReduced || !is3ColumnsLayout) && (
|
||||
|
@ -148,7 +149,7 @@ export const MainPanel = (): JSX.Element => {
|
|||
|
||||
<NavOption
|
||||
url="/archives"
|
||||
icon={Icon.Inventory}
|
||||
icon={Icon.Inventory2}
|
||||
title={langui.archives}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
@ -160,7 +161,7 @@ export const MainPanel = (): JSX.Element => {
|
|||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
{mainPanelReduced && is3ColumnsLayout ? "" : <HorizontalLine />}
|
||||
{(!mainPanelReduced || !is3ColumnsLayout) && <HorizontalLine />}
|
||||
|
||||
<div className={cJoin("text-center", cIf(mainPanelReduced && is3ColumnsLayout, "hidden"))}>
|
||||
{isDefinedAndNotEmpty(langui.licensing_notice) && (
|
||||
|
@ -172,22 +173,19 @@ export const MainPanel = (): JSX.Element => {
|
|||
<a
|
||||
onClick={() => sendAnalytics("MainPanel", "Visit license")}
|
||||
aria-label="Read more about the license we use for this website"
|
||||
className="group grid grid-flow-col place-content-center gap-1 transition-[filter]"
|
||||
className="group grid grid-flow-col place-content-center gap-1 transition-filter"
|
||||
href="https://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<div
|
||||
className="aspect-square w-6 bg-black transition-colors
|
||||
![mask-size:contain] ![mask-repeat:no-repeat] ![mask-position:center]
|
||||
[mask:url('/icons/creative-commons-brands.svg')] group-hover:bg-dark"
|
||||
<ColoredSvg
|
||||
className="h-6 w-6 bg-black group-hover:bg-dark"
|
||||
src="/icons/creative-commons-brands.svg"
|
||||
/>
|
||||
<div
|
||||
className="aspect-square w-6 bg-black transition-colors
|
||||
![mask-size:contain] ![mask-repeat:no-repeat] ![mask-position:center]
|
||||
[mask:url('/icons/creative-commons-by-brands.svg')] group-hover:bg-dark"
|
||||
<ColoredSvg
|
||||
className="h-6 w-6 bg-black group-hover:bg-dark"
|
||||
src="/icons/creative-commons-by-brands.svg"
|
||||
/>
|
||||
<div
|
||||
className="aspect-square w-6 bg-black transition-colors
|
||||
![mask-size:contain] ![mask-repeat:no-repeat] ![mask-position:center]
|
||||
[mask:url('/icons/creative-commons-sa-brands.svg')] group-hover:bg-dark"
|
||||
<ColoredSvg
|
||||
className="h-6 w-6 bg-black group-hover:bg-dark"
|
||||
src="/icons/creative-commons-sa-brands.svg"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -200,30 +198,36 @@ export const MainPanel = (): JSX.Element => {
|
|||
<a
|
||||
aria-label="Browse our GitHub repository, which include this website source code"
|
||||
onClick={() => sendAnalytics("MainPanel", "Visit GitHub")}
|
||||
className="aspect-square w-10 bg-black
|
||||
transition-colors ![mask-size:contain] ![mask-repeat:no-repeat]
|
||||
![mask-position:center] [mask:url('/icons/github-brands.svg')] hover:bg-dark"
|
||||
href="https://github.com/Accords-Library"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"></a>
|
||||
rel="noopener noreferrer">
|
||||
<ColoredSvg
|
||||
className="h-10 w-10 bg-black hover:bg-dark"
|
||||
src="/icons/github-brands.svg"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
aria-label="Follow us on Twitter"
|
||||
onClick={() => sendAnalytics("MainPanel", "Visit Twitter")}
|
||||
className="aspect-square w-10
|
||||
bg-black transition-colors ![mask-size:contain] ![mask-repeat:no-repeat]
|
||||
![mask-position:center] [mask:url('/icons/twitter-brands.svg')] hover:bg-dark"
|
||||
href="https://twitter.com/AccordsLibrary"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"></a>
|
||||
rel="noopener noreferrer">
|
||||
<ColoredSvg
|
||||
className="h-10 w-10 bg-black hover:bg-dark"
|
||||
src="/icons/twitter-brands.svg"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
aria-label="Join our Discord server!"
|
||||
onClick={() => sendAnalytics("MainPanel", "Visit Discord")}
|
||||
className="aspect-square w-10
|
||||
bg-black transition-colors ![mask-size:contain] ![mask-repeat:no-repeat]
|
||||
![mask-position:center] [mask:url('/icons/discord-brands.svg')] hover:bg-dark"
|
||||
href="/discord"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"></a>
|
||||
rel="noopener noreferrer">
|
||||
<ColoredSvg
|
||||
className="h-10 w-10 bg-black hover:bg-dark"
|
||||
src="/icons/discord-brands.svg"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import { useMemo } from "react";
|
||||
import UAParser from "ua-parser-js";
|
||||
import { useIsClient, useSessionStorage } from "usehooks-ts";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { Popup } from "components/Popup";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
|
||||
export const SafariPopup = (): JSX.Element => {
|
||||
const [hasDisgardedSafariWarning, setHasDisgardedSafariWarning] = useSessionStorage(
|
||||
"hasDisgardedSafariWarning",
|
||||
false
|
||||
);
|
||||
|
||||
const isClient = useIsClient();
|
||||
const isSafari = useMemo<boolean>(() => {
|
||||
if (isClient) {
|
||||
const parser = new UAParser();
|
||||
return parser.getBrowser().name === "Safari" || parser.getOS().name === "iOS";
|
||||
}
|
||||
return false;
|
||||
}, [isClient]);
|
||||
|
||||
return (
|
||||
<Popup isVisible={isSafari && !hasDisgardedSafariWarning}>
|
||||
<h1 className="text-2xl">Hi, you are using Safari!</h1>
|
||||
<p className="max-w-lg text-center">
|
||||
In most cases this wouldn’t be a problem but our website is—for some obscure
|
||||
reason—performing terribly on Safari (WebKit). Because of that, we have decided to display
|
||||
this message instead of letting you have a slow and painful experience. We are looking into
|
||||
the problem, and are hoping to fix this soon.
|
||||
</p>
|
||||
<p>In the meanwhile, if you are using an iPhone/iPad, please try using another device.</p>
|
||||
<p>If you are on macOS, please use another browser such as Firefox or Chrome.</p>
|
||||
|
||||
<Button
|
||||
text="Let me in regardless"
|
||||
className="mt-8"
|
||||
onClick={() => {
|
||||
setHasDisgardedSafariWarning(true);
|
||||
sendAnalytics("Safari", "Disgard warning");
|
||||
}}
|
||||
/>
|
||||
</Popup>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,237 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Icon } from "components/Ico";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
||||
import { OrderableList } from "components/Inputs/OrderableList";
|
||||
import { Select } from "components/Inputs/Select";
|
||||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { Popup } from "components/Popup";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { prettyLanguage } from "helpers/formatters";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
export const SettingsPopup = (): JSX.Element => {
|
||||
const {
|
||||
currency,
|
||||
darkMode,
|
||||
dyslexic,
|
||||
fontSize,
|
||||
playerName,
|
||||
preferredLanguages,
|
||||
selectedThemeMode,
|
||||
setCurrency,
|
||||
setDarkMode,
|
||||
setDyslexic,
|
||||
setFontSize,
|
||||
setPlayerName,
|
||||
setPreferredLanguages,
|
||||
setSelectedThemeMode,
|
||||
} = useUserSettings();
|
||||
|
||||
const { langui, currencies, languages } = useLocalData();
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const router = useRouter();
|
||||
const { configPanelOpen, setConfigPanelOpen } = useAppLayout();
|
||||
|
||||
const currencyOptions = useMemo(
|
||||
() =>
|
||||
filterHasAttributes(currencies, ["attributes"] as const).map(
|
||||
(currentCurrency) => currentCurrency.attributes.code
|
||||
),
|
||||
[currencies]
|
||||
);
|
||||
|
||||
const [currencySelect, setCurrencySelect] = useState<number>(-1);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(currency)) setCurrencySelect(currencyOptions.indexOf(currency));
|
||||
}, [currency, currencyOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currencySelect >= 0) setCurrency(currencyOptions[currencySelect]);
|
||||
}, [currencyOptions, currencySelect, setCurrency]);
|
||||
|
||||
return (
|
||||
<Popup
|
||||
isVisible={configPanelOpen}
|
||||
onCloseRequest={() => {
|
||||
setConfigPanelOpen(false);
|
||||
sendAnalytics("Settings", "Close settings");
|
||||
}}>
|
||||
<h2 className="text-2xl">{langui.settings}</h2>
|
||||
|
||||
<div
|
||||
className={cJoin(
|
||||
`mt-4 grid justify-items-center gap-16 text-center`,
|
||||
cIf(!is1ColumnLayout, "grid-cols-[auto_auto]")
|
||||
)}>
|
||||
{router.locales && (
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.languages}</h3>
|
||||
{preferredLanguages.length > 0 && (
|
||||
<OrderableList
|
||||
items={preferredLanguages.map((locale) => ({
|
||||
code: locale,
|
||||
name: prettyLanguage(locale, languages),
|
||||
}))}
|
||||
insertLabels={[
|
||||
{
|
||||
insertAt: 0,
|
||||
name: langui.primary_language ?? "Primary language",
|
||||
},
|
||||
{
|
||||
insertAt: 1,
|
||||
name: langui.secondary_language ?? "Secondary languages",
|
||||
},
|
||||
]}
|
||||
onChange={(items) => {
|
||||
const newPreferredLanguages = items.map((item) => item.code);
|
||||
setPreferredLanguages(newPreferredLanguages);
|
||||
sendAnalytics("Settings", "Change preferred languages");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={cJoin(
|
||||
"grid place-items-center gap-8 text-center",
|
||||
cIf(!is1ColumnLayout, "grid-cols-2")
|
||||
)}>
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.theme}</h3>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{
|
||||
onClick: () => {
|
||||
setDarkMode(false);
|
||||
setSelectedThemeMode(true);
|
||||
sendAnalytics("Settings", "Change theme (light)");
|
||||
},
|
||||
active: selectedThemeMode && !darkMode,
|
||||
text: langui.light,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setSelectedThemeMode(false);
|
||||
sendAnalytics("Settings", "Change theme (auto)");
|
||||
},
|
||||
active: !selectedThemeMode,
|
||||
text: langui.auto,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setDarkMode(true);
|
||||
setSelectedThemeMode(true);
|
||||
sendAnalytics("Settings", "Change theme (dark)");
|
||||
},
|
||||
active: selectedThemeMode && darkMode,
|
||||
text: langui.dark,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.currency}</h3>
|
||||
<div>
|
||||
<Select
|
||||
options={currencyOptions}
|
||||
value={currencySelect}
|
||||
onChange={(newCurrency) => {
|
||||
setCurrencySelect(newCurrency);
|
||||
sendAnalytics("Settings", `Change currency (${currencyOptions[newCurrency]})}`);
|
||||
}}
|
||||
className="w-28"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.font_size}</h3>
|
||||
<ButtonGroup
|
||||
buttonsProps={[
|
||||
{
|
||||
onClick: () => {
|
||||
setFontSize((current) => current / 1.05);
|
||||
sendAnalytics(
|
||||
"Settings",
|
||||
`Change font size (${((fontSize / 1.05) * 100).toLocaleString(undefined, {
|
||||
maximumFractionDigits: 0,
|
||||
})}%)`
|
||||
);
|
||||
},
|
||||
icon: Icon.TextDecrease,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setFontSize(1);
|
||||
sendAnalytics("Settings", "Change font size (100%)");
|
||||
},
|
||||
text: `${(fontSize * 100).toLocaleString(undefined, {
|
||||
maximumFractionDigits: 0,
|
||||
})}%`,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setFontSize((current) => current * 1.05);
|
||||
sendAnalytics(
|
||||
"Settings",
|
||||
`Change font size (${(fontSize * 1.05 * 100).toLocaleString(undefined, {
|
||||
maximumFractionDigits: 0,
|
||||
})}%)`
|
||||
);
|
||||
},
|
||||
icon: Icon.TextIncrease,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.font}</h3>
|
||||
<div className="grid gap-2">
|
||||
<Button
|
||||
active={!dyslexic}
|
||||
onClick={() => {
|
||||
setDyslexic(false);
|
||||
sendAnalytics("Settings", "Change font (Zen Maru Gothic)");
|
||||
}}
|
||||
className="font-zenMaruGothic"
|
||||
text="Zen Maru Gothic"
|
||||
/>
|
||||
<Button
|
||||
active={dyslexic}
|
||||
onClick={() => {
|
||||
setDyslexic(true);
|
||||
sendAnalytics("Settings", "Change font (OpenDyslexic)");
|
||||
}}
|
||||
className="font-openDyslexic"
|
||||
text="OpenDyslexic"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl">{langui.player_name}</h3>
|
||||
<TextInput
|
||||
placeholder="<player>"
|
||||
className="w-48"
|
||||
value={playerName}
|
||||
onChange={(newName) => {
|
||||
setPlayerName(newName);
|
||||
sendAnalytics("Settings", "Change username");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useIsSubPanelAtLeast } from "hooks/useContainerQuery";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -13,12 +13,12 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const SubPanel = ({ children }: Props): JSX.Element => {
|
||||
const isSubPanelAtLeastSm = useIsSubPanelAtLeast("2xs");
|
||||
const { isSubPanelAtLeastXs } = useContainerQueries();
|
||||
return (
|
||||
<div
|
||||
className={cJoin(
|
||||
"grid gap-y-2 text-center",
|
||||
cIf(isSubPanelAtLeastSm, "px-10 pt-10 pb-20", "p-4")
|
||||
cIf(isSubPanelAtLeastXs, "px-10 pt-10 pb-20", "p-4")
|
||||
)}>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect } from "react";
|
||||
import Hotkeys from "react-hot-keys";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
|
@ -9,8 +9,8 @@ import { cIf, cJoin } from "helpers/className";
|
|||
*/
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
state: boolean;
|
||||
onCloseRequest?: () => void;
|
||||
isVisible: boolean;
|
||||
children: React.ReactNode;
|
||||
fillViewport?: boolean;
|
||||
hideBackground?: boolean;
|
||||
|
@ -20,8 +20,8 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Popup = ({
|
||||
onClose,
|
||||
state,
|
||||
onCloseRequest,
|
||||
isVisible,
|
||||
children,
|
||||
fillViewport,
|
||||
hideBackground = false,
|
||||
|
@ -29,36 +29,36 @@ export const Popup = ({
|
|||
}: Props): JSX.Element => {
|
||||
const { setMenuGestures } = useAppLayout();
|
||||
|
||||
useHotkeys("escape", () => onCloseRequest?.(), {}, [onCloseRequest]);
|
||||
|
||||
useEffect(() => {
|
||||
setMenuGestures(!state);
|
||||
}, [setMenuGestures, state]);
|
||||
setMenuGestures(!isVisible);
|
||||
}, [setMenuGestures, isVisible]);
|
||||
|
||||
return (
|
||||
<Hotkeys keyName="escape" allowRepeat onKeyDown={onClose}>
|
||||
<div
|
||||
className={cJoin(
|
||||
"fixed inset-0 z-50 grid place-content-center transition-[backdrop-filter] duration-500",
|
||||
cIf(state, "[backdrop-filter:blur(2px)]", "pointer-events-none touch-none")
|
||||
"fixed inset-0 z-50 grid place-content-center transition-filter duration-500",
|
||||
cIf(isVisible, "backdrop-blur", "pointer-events-none touch-none")
|
||||
)}>
|
||||
<div
|
||||
className={cJoin(
|
||||
"fixed inset-0 bg-shade transition-all duration-500",
|
||||
cIf(state, "bg-opacity-50", "bg-opacity-0")
|
||||
cIf(isVisible, "bg-opacity-50", "bg-opacity-0")
|
||||
)}
|
||||
onClick={onClose}
|
||||
onClick={onCloseRequest}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cJoin(
|
||||
"grid place-items-center gap-4 transition-transform",
|
||||
cIf(padding, "p-10"),
|
||||
cIf(state, "scale-100", "scale-0"),
|
||||
cIf(isVisible, "scale-100", "scale-0"),
|
||||
cIf(fillViewport, "absolute inset-10", "relative max-h-[80vh] overflow-y-auto"),
|
||||
cIf(!hideBackground, "rounded-lg bg-light shadow-2xl shadow-shade")
|
||||
)}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</Hotkeys>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -32,7 +32,6 @@ interface Props {
|
|||
topChips?: string[];
|
||||
bottomChips?: string[];
|
||||
keepInfoVisible?: boolean;
|
||||
stackNumber?: number;
|
||||
metadata?: {
|
||||
releaseDate?: DatePickerFragment | null;
|
||||
releaseDateFormat?: Intl.DateTimeFormatOptions["dateStyle"];
|
||||
|
@ -62,7 +61,6 @@ export const PreviewCard = ({
|
|||
title,
|
||||
subtitle,
|
||||
description,
|
||||
stackNumber = 0,
|
||||
topChips,
|
||||
bottomChips,
|
||||
keepInfoVisible,
|
||||
|
@ -116,32 +114,6 @@ export const PreviewCard = ({
|
|||
href={href}
|
||||
className="group grid cursor-pointer items-end text-left transition-transform
|
||||
drop-shadow-shade-xl hover:scale-[1.02]">
|
||||
{stackNumber > 0 && (
|
||||
<>
|
||||
<div
|
||||
className={cJoin(
|
||||
`absolute inset-0 scale-[.85] overflow-hidden bg-light brightness-[0.8] sepia-[0.5]
|
||||
transition-[top_transform] group-hover:-top-9`,
|
||||
cIf(thumbnailRounded, "rounded-md")
|
||||
)}>
|
||||
{thumbnail && (
|
||||
<Img className="opacity-30" src={thumbnail} quality={ImageQuality.Medium} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cJoin(
|
||||
`absolute inset-0 overflow-hidden bg-light brightness-[0.9] sepia-[0.2]
|
||||
transition-[top_transform] group-hover:-top-4 group-hover:scale-[.94]`,
|
||||
cIf(thumbnailRounded, "rounded-md")
|
||||
)}>
|
||||
{thumbnail && (
|
||||
<Img className="opacity-70" src={thumbnail} quality={ImageQuality.Medium} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{thumbnail ? (
|
||||
<div
|
||||
className="relative"
|
||||
|
@ -159,13 +131,7 @@ export const PreviewCard = ({
|
|||
src={thumbnail}
|
||||
quality={ImageQuality.Medium}
|
||||
/>
|
||||
{stackNumber > 0 && (
|
||||
<div
|
||||
className="absolute right-2 top-2 rounded-full bg-black
|
||||
bg-opacity-60 px-2 text-light">
|
||||
{stackNumber}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hoverlay && hoverlay.__typename === "Video" && (
|
||||
<>
|
||||
<div
|
||||
|
@ -190,15 +156,8 @@ export const PreviewCard = ({
|
|||
className={cJoin(
|
||||
"relative w-full bg-light",
|
||||
cIf(keepInfoVisible, "rounded-t-md", "rounded-md notHoverable:rounded-b-none")
|
||||
)}>
|
||||
{stackNumber > 0 && (
|
||||
<div
|
||||
className="absolute right-2 top-2 rounded-full bg-black
|
||||
bg-opacity-60 px-2 text-light">
|
||||
{stackNumber}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={cJoin(
|
||||
|
|
|
@ -6,9 +6,9 @@ import { Ico, Icon } from "./Ico";
|
|||
import { cJoin } from "helpers/className";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
|
||||
import { Ids } from "types/ids";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
interface Group<T> {
|
||||
name: string;
|
||||
|
@ -191,7 +191,7 @@ export const SmartList = <T,>({
|
|||
)}
|
||||
<div
|
||||
className={cJoin(
|
||||
`grid items-start gap-8 border-b-[3px] border-dotted pb-12
|
||||
`grid items-start gap-8 border-b-2 border-dotted pb-12
|
||||
last-of-type:border-0`,
|
||||
className
|
||||
)}>
|
||||
|
@ -222,7 +222,7 @@ export const SmartList = <T,>({
|
|||
*/
|
||||
|
||||
const DefaultRenderWhenEmpty = () => {
|
||||
const is3ColumnsLayout = useIs3ColumnsLayout();
|
||||
const { is3ColumnsLayout } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
return (
|
||||
<div className="grid h-full place-content-center">
|
||||
|
|
|
@ -4,10 +4,10 @@ import { InsetBox } from "components/InsetBox";
|
|||
import { Markdawn } from "components/Markdown/Markdawn";
|
||||
import { GetContentTextQuery, UploadImageFragment } from "graphql/generated";
|
||||
import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useLightBox } from "contexts/LightBoxContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -42,12 +42,11 @@ export const ThumbnailHeader = ({
|
|||
description,
|
||||
languageSwitcher,
|
||||
}: Props): JSX.Element => {
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
const { langui } = useLocalData();
|
||||
const { showLightBox } = useLightBox();
|
||||
|
||||
return (
|
||||
<>
|
||||
<LightBox />
|
||||
<div className="mb-12 grid place-items-center gap-12">
|
||||
<div className="drop-shadow-shade-lg">
|
||||
{thumbnail ? (
|
||||
|
@ -55,9 +54,7 @@ export const ThumbnailHeader = ({
|
|||
className="cursor-pointer rounded-xl"
|
||||
src={thumbnail}
|
||||
quality={ImageQuality.Medium}
|
||||
onClick={() => {
|
||||
openLightBox([getAssetURL(thumbnail.url, ImageQuality.Large)]);
|
||||
}}
|
||||
onClick={() => showLightBox([thumbnail])}
|
||||
/>
|
||||
) : (
|
||||
<div className="aspect-[4/3] w-96 rounded-xl bg-light"></div>
|
||||
|
|
|
@ -4,9 +4,9 @@ import { ToolTip } from "components/ToolTip";
|
|||
import { getStatusDescription } from "helpers/others";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { useIsContentPanelNoMoreThan } from "hooks/useContainerQuery";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -30,7 +30,7 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const DefinitionCard = ({ source, translations = [], index, categories }: Props): JSX.Element => {
|
||||
const isContentPanelNoMoreThanMd = useIsContentPanelNoMoreThan("md");
|
||||
const { isContentPanelAtLeastMd } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: translations,
|
||||
|
@ -78,7 +78,7 @@ const DefinitionCard = ({ source, translations = [], index, categories }: Props)
|
|||
<div
|
||||
className={cJoin(
|
||||
"mt-3 flex place-items-center gap-2",
|
||||
cIf(isContentPanelNoMoreThanMd, "flex-col text-center")
|
||||
cIf(!isContentPanelAtLeastMd, "flex-col text-center")
|
||||
)}>
|
||||
<p>{langui.source}: </p>
|
||||
<Button href={source.url} size="small" text={source.name} />
|
||||
|
|
|
@ -8,7 +8,7 @@ import React, {
|
|||
useState,
|
||||
} from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useLocalStorage, useSessionStorage } from "usehooks-ts";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
import { useStateWithLocalStorage } from "hooks/useStateWithLocalStorage";
|
||||
|
@ -34,11 +34,6 @@ interface AppLayoutState {
|
|||
menuGestures: boolean;
|
||||
toggleMenuGestures: () => void;
|
||||
setMenuGestures: Dispatch<SetStateAction<AppLayoutState["menuGestures"]>>;
|
||||
|
||||
hasDisgardedSafariWarning: boolean;
|
||||
setHasDisgardedSafariWarning: Dispatch<
|
||||
SetStateAction<AppLayoutState["hasDisgardedSafariWarning"]>
|
||||
>;
|
||||
}
|
||||
|
||||
const initialState: RequiredNonNullable<AppLayoutState> = {
|
||||
|
@ -61,9 +56,6 @@ const initialState: RequiredNonNullable<AppLayoutState> = {
|
|||
menuGestures: true,
|
||||
toggleMenuGestures: () => null,
|
||||
setMenuGestures: () => null,
|
||||
|
||||
hasDisgardedSafariWarning: false,
|
||||
setHasDisgardedSafariWarning: () => null,
|
||||
};
|
||||
|
||||
const AppLayoutContext = createContext<AppLayoutState>(initialState);
|
||||
|
@ -99,11 +91,6 @@ export const AppContextProvider = ({ children }: Props): JSX.Element => {
|
|||
|
||||
const [menuGestures, setMenuGestures] = useState(false);
|
||||
|
||||
const [hasDisgardedSafariWarning, setHasDisgardedSafariWarning] = useSessionStorage(
|
||||
"hasDisgardedSafariWarning",
|
||||
initialState.hasDisgardedSafariWarning
|
||||
);
|
||||
|
||||
const toggleSubPanelOpen = () => {
|
||||
setSubPanelOpen((current) => (isDefined(current) ? !current : current));
|
||||
};
|
||||
|
@ -148,13 +135,11 @@ export const AppContextProvider = ({ children }: Props): JSX.Element => {
|
|||
mainPanelReduced,
|
||||
mainPanelOpen,
|
||||
menuGestures,
|
||||
hasDisgardedSafariWarning,
|
||||
setSubPanelOpen,
|
||||
setConfigPanelOpen,
|
||||
setMainPanelReduced,
|
||||
setMainPanelOpen,
|
||||
setMenuGestures,
|
||||
setHasDisgardedSafariWarning,
|
||||
toggleSubPanelOpen,
|
||||
toggleConfigPanelOpen,
|
||||
toggleMainPanelReduced,
|
||||
|
|
|
@ -1,33 +1,86 @@
|
|||
import React, {
|
||||
createContext,
|
||||
Dispatch,
|
||||
ReactNode,
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useState,
|
||||
} from "react";
|
||||
import React, { createContext, ReactNode, useContext, useMemo, useState } from "react";
|
||||
import { useUserSettings } from "./UserSettingsContext";
|
||||
import { useOnResize } from "hooks/useOnResize";
|
||||
import { Ids } from "types/ids";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
|
||||
interface ContainerQueriesState {
|
||||
screenWidth: number;
|
||||
setScreenWidth: Dispatch<SetStateAction<ContainerQueriesState["screenWidth"]>>;
|
||||
type Size =
|
||||
| "2xl"
|
||||
| "2xs"
|
||||
| "3xl"
|
||||
| "4xl"
|
||||
| "5xl"
|
||||
| "6xl"
|
||||
| "7xl"
|
||||
| "lg"
|
||||
| "md"
|
||||
| "sm"
|
||||
| "xl"
|
||||
| "xs";
|
||||
|
||||
contentPanelWidth: number;
|
||||
setContentPanelWidth: Dispatch<SetStateAction<ContainerQueriesState["contentPanelWidth"]>>;
|
||||
const sizes: Record<Size, number> = {
|
||||
"2xl": 42,
|
||||
"3xl": 48,
|
||||
"4xl": 56,
|
||||
"5xl": 64,
|
||||
"6xl": 72,
|
||||
"7xl": 80,
|
||||
lg: 32,
|
||||
md: 28,
|
||||
sm: 24,
|
||||
xl: 36,
|
||||
xs: 19,
|
||||
"2xs": 16,
|
||||
};
|
||||
|
||||
subPanelWidth: number;
|
||||
setSubPanelWidth: Dispatch<SetStateAction<ContainerQueriesState["subPanelWidth"]>>;
|
||||
}
|
||||
type ContainerQueriesContainers = "contentPanel" | "screen" | "subPanel";
|
||||
|
||||
type ContainerQueriesKeys =
|
||||
| "is1ColumnLayout"
|
||||
| "is3ColumnsLayout"
|
||||
| `is${Capitalize<ContainerQueriesContainers>}AtLeast${Capitalize<Size>}`;
|
||||
|
||||
type ContainerQueriesState = Record<ContainerQueriesKeys, boolean>;
|
||||
|
||||
const initialState: RequiredNonNullable<ContainerQueriesState> = {
|
||||
screenWidth: 0,
|
||||
setScreenWidth: () => null,
|
||||
|
||||
contentPanelWidth: 0,
|
||||
setContentPanelWidth: () => null,
|
||||
|
||||
subPanelWidth: 0,
|
||||
setSubPanelWidth: () => null,
|
||||
is1ColumnLayout: false,
|
||||
is3ColumnsLayout: false,
|
||||
isContentPanelAtLeast2xl: false,
|
||||
isContentPanelAtLeast2xs: false,
|
||||
isContentPanelAtLeast3xl: false,
|
||||
isContentPanelAtLeast4xl: false,
|
||||
isContentPanelAtLeast5xl: false,
|
||||
isContentPanelAtLeast6xl: false,
|
||||
isContentPanelAtLeast7xl: false,
|
||||
isContentPanelAtLeastLg: false,
|
||||
isContentPanelAtLeastMd: false,
|
||||
isContentPanelAtLeastSm: false,
|
||||
isContentPanelAtLeastXl: false,
|
||||
isContentPanelAtLeastXs: false,
|
||||
isScreenAtLeast2xl: false,
|
||||
isScreenAtLeast2xs: false,
|
||||
isScreenAtLeast3xl: false,
|
||||
isScreenAtLeast4xl: false,
|
||||
isScreenAtLeast5xl: false,
|
||||
isScreenAtLeast6xl: false,
|
||||
isScreenAtLeast7xl: false,
|
||||
isScreenAtLeastLg: false,
|
||||
isScreenAtLeastMd: false,
|
||||
isScreenAtLeastSm: false,
|
||||
isScreenAtLeastXl: false,
|
||||
isScreenAtLeastXs: false,
|
||||
isSubPanelAtLeast2xl: false,
|
||||
isSubPanelAtLeast2xs: false,
|
||||
isSubPanelAtLeast3xl: false,
|
||||
isSubPanelAtLeast4xl: false,
|
||||
isSubPanelAtLeast5xl: false,
|
||||
isSubPanelAtLeast6xl: false,
|
||||
isSubPanelAtLeast7xl: false,
|
||||
isSubPanelAtLeastLg: false,
|
||||
isSubPanelAtLeastMd: false,
|
||||
isSubPanelAtLeastSm: false,
|
||||
isSubPanelAtLeastXl: false,
|
||||
isSubPanelAtLeastXs: false,
|
||||
};
|
||||
|
||||
const ContainerQueriesContext = createContext<ContainerQueriesState>(initialState);
|
||||
|
@ -43,17 +96,220 @@ export const ContainerQueriesContextProvider = ({ children }: Props): JSX.Elemen
|
|||
const [contentPanelWidth, setContentPanelWidth] = useState(0);
|
||||
const [subPanelWidth, setSubPanelWidth] = useState(0);
|
||||
|
||||
useOnResize(Ids.Body, (width) => setScreenWidth(width));
|
||||
useOnResize(Ids.ContentPanel, (width) => setContentPanelWidth(width));
|
||||
useOnResize(Ids.SubPanel, (width) => setSubPanelWidth(width));
|
||||
|
||||
const { fontSize } = useUserSettings();
|
||||
|
||||
const screenAtLeasts = useMemo(
|
||||
() => ({
|
||||
isScreenAtLeast2xs: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("2xs")
|
||||
),
|
||||
isScreenAtLeastXs: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("xs")),
|
||||
isScreenAtLeastSm: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("sm")),
|
||||
isScreenAtLeastMd: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("md")),
|
||||
isScreenAtLeastLg: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("lg")),
|
||||
isScreenAtLeastXl: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("xl")),
|
||||
isScreenAtLeast2xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("2xl")
|
||||
),
|
||||
isScreenAtLeast3xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("3xl")
|
||||
),
|
||||
isScreenAtLeast4xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("4xl")
|
||||
),
|
||||
isScreenAtLeast5xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("5xl")
|
||||
),
|
||||
isScreenAtLeast6xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("6xl")
|
||||
),
|
||||
isScreenAtLeast7xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("7xl")
|
||||
),
|
||||
}),
|
||||
[screenWidth, fontSize]
|
||||
);
|
||||
|
||||
const columnLayouts = useMemo(
|
||||
() => ({
|
||||
is1ColumnLayout: !screenAtLeasts.isScreenAtLeast5xl,
|
||||
is3ColumnsLayout: screenAtLeasts.isScreenAtLeast5xl,
|
||||
}),
|
||||
[screenAtLeasts.isScreenAtLeast5xl]
|
||||
);
|
||||
|
||||
const subPanelAtLeasts = useMemo(
|
||||
() => ({
|
||||
isSubPanelAtLeast2xs: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("2xs")
|
||||
),
|
||||
isSubPanelAtLeastXs: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("xs")
|
||||
),
|
||||
isSubPanelAtLeastSm: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("sm")
|
||||
),
|
||||
isSubPanelAtLeastMd: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("md")
|
||||
),
|
||||
isSubPanelAtLeastLg: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("lg")
|
||||
),
|
||||
isSubPanelAtLeastXl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("xl")
|
||||
),
|
||||
isSubPanelAtLeast2xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("2xl")
|
||||
),
|
||||
isSubPanelAtLeast3xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("3xl")
|
||||
),
|
||||
isSubPanelAtLeast4xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("4xl")
|
||||
),
|
||||
isSubPanelAtLeast5xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("5xl")
|
||||
),
|
||||
isSubPanelAtLeast6xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("6xl")
|
||||
),
|
||||
isSubPanelAtLeast7xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("7xl")
|
||||
),
|
||||
}),
|
||||
[subPanelWidth, fontSize]
|
||||
);
|
||||
|
||||
const contentPanelAtLeasts = useMemo(
|
||||
() => ({
|
||||
isContentPanelAtLeast2xs: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("2xs")
|
||||
),
|
||||
isContentPanelAtLeastXs: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("xs")
|
||||
),
|
||||
isContentPanelAtLeastSm: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("sm")
|
||||
),
|
||||
isContentPanelAtLeastMd: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("md")
|
||||
),
|
||||
isContentPanelAtLeastLg: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("lg")
|
||||
),
|
||||
isContentPanelAtLeastXl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("xl")
|
||||
),
|
||||
isContentPanelAtLeast2xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("2xl")
|
||||
),
|
||||
isContentPanelAtLeast3xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("3xl")
|
||||
),
|
||||
isContentPanelAtLeast4xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("4xl")
|
||||
),
|
||||
isContentPanelAtLeast5xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("5xl")
|
||||
),
|
||||
isContentPanelAtLeast6xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("6xl")
|
||||
),
|
||||
isContentPanelAtLeast7xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("7xl")
|
||||
),
|
||||
}),
|
||||
[contentPanelWidth, fontSize]
|
||||
);
|
||||
|
||||
return (
|
||||
<ContainerQueriesContext.Provider
|
||||
value={{
|
||||
screenWidth,
|
||||
contentPanelWidth,
|
||||
subPanelWidth,
|
||||
setScreenWidth,
|
||||
setContentPanelWidth,
|
||||
setSubPanelWidth,
|
||||
...screenAtLeasts,
|
||||
...contentPanelAtLeasts,
|
||||
...subPanelAtLeasts,
|
||||
...columnLayouts,
|
||||
}}>
|
||||
{children}
|
||||
</ContainerQueriesContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
type MediaQuery = { value: number; unit: "px" | "rem"; rule: "max" | "min" };
|
||||
|
||||
const createAtLeastMediaQuery = (size: Size): MediaQuery => ({
|
||||
value: sizes[size],
|
||||
unit: "rem",
|
||||
rule: "min",
|
||||
});
|
||||
|
||||
const applyContainerQuery = (fontSize: number, width: number, query: MediaQuery): boolean => {
|
||||
const breakpoint = query.value * (query.unit === "rem" ? 16 : 1) * fontSize;
|
||||
return query.rule === "min" ? width >= breakpoint : width < breakpoint;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import React, { createContext, ReactNode, useCallback, useContext, useState } from "react";
|
||||
import { UploadImageFragment } from "graphql/generated";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
import { LightBox } from "components/LightBox";
|
||||
import { filterDefined } from "helpers/others";
|
||||
|
||||
type LightBoxImagesNullable = (UploadImageFragment | string | null | undefined)[];
|
||||
type LightBoxImages = (UploadImageFragment | string)[];
|
||||
|
||||
interface LightBoxState {
|
||||
showLightBox: (images: LightBoxImagesNullable, index?: number) => void;
|
||||
}
|
||||
|
||||
const initialState: RequiredNonNullable<LightBoxState> = {
|
||||
showLightBox: () => null,
|
||||
};
|
||||
|
||||
const LightBoxContext = createContext<LightBoxState>(initialState);
|
||||
|
||||
export const useLightBox = (): LightBoxState => useContext(LightBoxContext);
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const LightBoxContextProvider = ({ children }: Props): JSX.Element => {
|
||||
const [isLightBoxVisible, setLightBoxVisibility] = useState(false);
|
||||
const [lightBoxImages, setLightBoxImages] = useState<LightBoxImages>([]);
|
||||
const [lightBoxIndex, setLightBoxIndex] = useState(0);
|
||||
|
||||
const showLightBox = useCallback((images: LightBoxImagesNullable, index = 0) => {
|
||||
const filteredImages = filterDefined(images);
|
||||
setLightBoxIndex(index);
|
||||
setLightBoxImages(filteredImages);
|
||||
setLightBoxVisibility(true);
|
||||
}, []);
|
||||
|
||||
const closeLightBox = useCallback(() => {
|
||||
setLightBoxVisibility(false);
|
||||
setTimeout(() => setLightBoxImages([]), 100);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LightBoxContext.Provider value={{ showLightBox }}>
|
||||
<LightBox
|
||||
isVisible={isLightBoxVisible}
|
||||
onCloseRequest={closeLightBox}
|
||||
image={lightBoxImages[lightBoxIndex]}
|
||||
isNextImageAvailable={lightBoxIndex < lightBoxImages.length - 1}
|
||||
isPreviousImageAvailable={lightBoxIndex > 0}
|
||||
onPressNext={() => setLightBoxIndex((current) => current + 1)}
|
||||
onPressPrevious={() => setLightBoxIndex((current) => current - 1)}
|
||||
/>
|
||||
|
||||
{children}
|
||||
</LightBoxContext.Provider>
|
||||
);
|
||||
};
|
|
@ -13,6 +13,7 @@ import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
|||
import { RequiredNonNullable } from "types/types";
|
||||
import { getDefaultPreferredLanguages } from "helpers/locales";
|
||||
import { useDarkMode } from "hooks/useDarkMode";
|
||||
import { SettingsPopup } from "components/Panels/SettingsPopup";
|
||||
|
||||
interface UserSettingsState {
|
||||
fontSize: number;
|
||||
|
@ -134,6 +135,32 @@ export const UserSettingsProvider = ({ children }: Props): JSX.Element => {
|
|||
}
|
||||
}, [fontSize]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const next = document.getElementById("__next");
|
||||
if (isDefined(next)) {
|
||||
if (darkMode) {
|
||||
next.classList.add("set-theme-dark");
|
||||
next.classList.remove("set-theme-light");
|
||||
} else {
|
||||
next.classList.add("set-theme-light");
|
||||
next.classList.remove("set-theme-dark");
|
||||
}
|
||||
}
|
||||
}, [darkMode]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const next = document.getElementById("__next");
|
||||
if (isDefined(next)) {
|
||||
if (dyslexic) {
|
||||
next.classList.add("set-theme-font-dyslexic");
|
||||
next.classList.remove("set-theme-font-standard");
|
||||
} else {
|
||||
next.classList.add("set-theme-font-standard");
|
||||
next.classList.remove("set-theme-font-dyslexic");
|
||||
}
|
||||
}
|
||||
}, [dyslexic]);
|
||||
|
||||
return (
|
||||
<UserSettingsContext.Provider
|
||||
value={{
|
||||
|
@ -155,6 +182,7 @@ export const UserSettingsProvider = ({ children }: Props): JSX.Element => {
|
|||
toggleSelectedThemeMode,
|
||||
toggleDyslexic,
|
||||
}}>
|
||||
<SettingsPopup />
|
||||
{children}
|
||||
</UserSettingsContext.Provider>
|
||||
);
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
|
||||
type MediaQuery = { value: number; unit: "px" | "rem"; rule: "max" | "min" };
|
||||
|
||||
type Size =
|
||||
| "2xl"
|
||||
| "2xs"
|
||||
| "3xl"
|
||||
| "4xl"
|
||||
| "5xl"
|
||||
| "6xl"
|
||||
| "7xl"
|
||||
| "lg"
|
||||
| "md"
|
||||
| "sm"
|
||||
| "xl"
|
||||
| "xs";
|
||||
|
||||
const sizes: Record<Size, number> = {
|
||||
"2xl": 42,
|
||||
"3xl": 48,
|
||||
"4xl": 56,
|
||||
"5xl": 64,
|
||||
"6xl": 72,
|
||||
"7xl": 80,
|
||||
lg: 32,
|
||||
md: 28,
|
||||
sm: 24,
|
||||
xl: 36,
|
||||
xs: 20,
|
||||
"2xs": 16,
|
||||
};
|
||||
|
||||
export const useIsScreenAtLeast = (size: Size): boolean => {
|
||||
const { screenWidth } = useContainerQueries();
|
||||
return useApplyContainerQuery(screenWidth, {
|
||||
value: sizes[size],
|
||||
unit: "rem",
|
||||
rule: "min",
|
||||
});
|
||||
};
|
||||
|
||||
// ts-unused-exports:disable-next-line
|
||||
export const useIsScreenNoMoreThan = (size: Size): boolean => {
|
||||
const { screenWidth } = useContainerQueries();
|
||||
return useApplyContainerQuery(screenWidth, {
|
||||
value: sizes[size],
|
||||
unit: "rem",
|
||||
rule: "max",
|
||||
});
|
||||
};
|
||||
|
||||
export const useIsContentPanelAtLeast = (size: Size): boolean => {
|
||||
const { contentPanelWidth } = useContainerQueries();
|
||||
return useApplyContainerQuery(contentPanelWidth, {
|
||||
value: sizes[size],
|
||||
unit: "rem",
|
||||
rule: "min",
|
||||
});
|
||||
};
|
||||
|
||||
export const useIsContentPanelNoMoreThan = (size: Size): boolean => {
|
||||
const { contentPanelWidth } = useContainerQueries();
|
||||
return useApplyContainerQuery(contentPanelWidth, {
|
||||
value: sizes[size],
|
||||
unit: "rem",
|
||||
rule: "max",
|
||||
});
|
||||
};
|
||||
|
||||
export const useIsSubPanelAtLeast = (size: Size): boolean => {
|
||||
const { subPanelWidth } = useContainerQueries();
|
||||
return useApplyContainerQuery(subPanelWidth, {
|
||||
value: sizes[size],
|
||||
unit: "rem",
|
||||
rule: "min",
|
||||
});
|
||||
};
|
||||
|
||||
// ts-unused-exports:disable-next-line
|
||||
export const useIsSubPanelNoMoreThan = (size: Size): boolean => {
|
||||
const { subPanelWidth } = useContainerQueries();
|
||||
return useApplyContainerQuery(subPanelWidth, {
|
||||
value: sizes[size],
|
||||
unit: "rem",
|
||||
rule: "max",
|
||||
});
|
||||
};
|
||||
|
||||
export const useIs3ColumnsLayout = (): boolean => {
|
||||
const { screenWidth } = useContainerQueries();
|
||||
return useApplyContainerQuery(screenWidth, {
|
||||
value: sizes["5xl"],
|
||||
unit: "rem",
|
||||
rule: "min",
|
||||
});
|
||||
};
|
||||
|
||||
export const useIs1ColumnLayout = (): boolean => {
|
||||
const { screenWidth } = useContainerQueries();
|
||||
return useApplyContainerQuery(screenWidth, {
|
||||
value: sizes["5xl"],
|
||||
unit: "rem",
|
||||
rule: "max",
|
||||
});
|
||||
};
|
||||
|
||||
const useApplyContainerQuery = (width: number, query: MediaQuery) => {
|
||||
const { fontSize } = useUserSettings();
|
||||
const breakpoint = query.value * (query.unit === "rem" ? 16 : 1) * fontSize;
|
||||
return query.rule === "min" ? width >= breakpoint : width < breakpoint;
|
||||
};
|
|
@ -4,21 +4,27 @@ import { isDefined } from "helpers/others";
|
|||
|
||||
export const useFullscreen = (
|
||||
id: string
|
||||
): { isFullscreen: boolean; toggleFullscreen: () => void } => {
|
||||
): {
|
||||
isFullscreen: boolean;
|
||||
requestFullscreen: () => void;
|
||||
exitFullscreen: () => void;
|
||||
toggleFullscreen: () => void;
|
||||
} => {
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
const isClient = useIsClient();
|
||||
|
||||
const elem = useMemo(() => (isClient ? document.querySelector(`#${id}`) : null), [id, isClient]);
|
||||
|
||||
const toggleFullscreen = useCallback(() => {
|
||||
if (elem) {
|
||||
if (isFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else {
|
||||
elem.requestFullscreen();
|
||||
}
|
||||
}
|
||||
}, [elem, isFullscreen]);
|
||||
const requestFullscreen = useCallback(() => elem?.requestFullscreen(), [elem]);
|
||||
const exitFullscreen = useCallback(
|
||||
async () => isFullscreen && document.exitFullscreen(),
|
||||
[isFullscreen]
|
||||
);
|
||||
|
||||
const toggleFullscreen = useCallback(
|
||||
() => (isFullscreen ? exitFullscreen() : requestFullscreen()),
|
||||
[exitFullscreen, isFullscreen, requestFullscreen]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const onFullscreenChanged = () => {
|
||||
|
@ -28,5 +34,5 @@ export const useFullscreen = (
|
|||
return () => removeEventListener("fullscreenchange", onFullscreenChanged);
|
||||
}, []);
|
||||
|
||||
return { isFullscreen, toggleFullscreen };
|
||||
return { isFullscreen, requestFullscreen, exitFullscreen, toggleFullscreen };
|
||||
};
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { LightBox } from "components/LightBox";
|
||||
|
||||
export const useLightBox = (): [(images: string[], index?: number) => void, () => JSX.Element] => {
|
||||
const [lightboxOpen, setLightboxOpen] = useState(false);
|
||||
const [lightboxImages, setLightboxImages] = useState([""]);
|
||||
const [lightboxIndex, setLightboxIndex] = useState(0);
|
||||
|
||||
return [
|
||||
(images: string[], index = 0) => {
|
||||
setLightboxOpen(true);
|
||||
setLightboxImages(images);
|
||||
setLightboxIndex(index);
|
||||
},
|
||||
() => (
|
||||
<LightBox
|
||||
state={lightboxOpen}
|
||||
setState={setLightboxOpen}
|
||||
images={lightboxImages}
|
||||
index={lightboxIndex}
|
||||
setIndex={setLightboxIndex}
|
||||
/>
|
||||
),
|
||||
];
|
||||
};
|
|
@ -1,29 +1,49 @@
|
|||
import "@fontsource/material-icons";
|
||||
import "@fontsource/material-icons-outlined";
|
||||
import "@fontsource/opendyslexic/400.css";
|
||||
import "@fontsource/share-tech-mono/400.css";
|
||||
import "@fontsource/opendyslexic/700.css";
|
||||
import "@fontsource/vollkorn/700.css";
|
||||
import "@fontsource/zen-maru-gothic/500.css";
|
||||
import "@fontsource/zen-maru-gothic/900.css";
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
import Script from "next/script";
|
||||
import { AppContextProvider } from "contexts/AppLayoutContext";
|
||||
import "tailwind.css";
|
||||
|
||||
import "styles/animations.css";
|
||||
import "styles/custom-classes.css";
|
||||
import "styles/debug.css";
|
||||
import "styles/formatted.css";
|
||||
import "styles/others.css";
|
||||
import "styles/rc-slider.css";
|
||||
import "styles/tippy.css";
|
||||
|
||||
import { TerminalContextProvider } from "contexts/TerminalContext";
|
||||
import { UserSettingsProvider as UserSettingsContextProvider } from "contexts/UserSettingsContext";
|
||||
import { LocalDataContextProvider } from "contexts/LocalDataContext";
|
||||
import { ContainerQueriesContextProvider } from "contexts/ContainerQueriesContext";
|
||||
import { LightBoxContextProvider } from "contexts/LightBoxContext";
|
||||
|
||||
const AccordsLibraryApp = (props: AppProps): JSX.Element => (
|
||||
<AppContextProvider>
|
||||
<ContainerQueriesContextProvider>
|
||||
<LocalDataContextProvider>
|
||||
<AppContextProvider>
|
||||
<UserSettingsContextProvider>
|
||||
<ContainerQueriesContextProvider>
|
||||
<TerminalContextProvider>
|
||||
<LightBoxContextProvider>
|
||||
<Script
|
||||
async
|
||||
defer
|
||||
data-website-id={process.env.NEXT_PUBLIC_UMAMI_ID}
|
||||
src={`${process.env.NEXT_PUBLIC_UMAMI_URL}/umami.js`}
|
||||
/>
|
||||
<props.Component {...props.pageProps} />
|
||||
</LightBoxContextProvider>
|
||||
</TerminalContextProvider>
|
||||
</UserSettingsContextProvider>
|
||||
</LocalDataContextProvider>
|
||||
</ContainerQueriesContextProvider>
|
||||
</UserSettingsContextProvider>
|
||||
</AppContextProvider>
|
||||
</LocalDataContextProvider>
|
||||
);
|
||||
export default AccordsLibraryApp;
|
||||
|
|
|
@ -6,9 +6,9 @@ import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps"
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { randomInt } from "helpers/numbers";
|
||||
import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
|
||||
import { useIs1ColumnLayout } from "hooks/useContainerQuery";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -18,7 +18,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||
const router = useRouter();
|
||||
const { langui } = useLocalData();
|
||||
const is1ColumnLayout = useIs1ColumnLayout();
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const [formResponse, setFormResponse] = useState("");
|
||||
const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">("stale");
|
||||
|
||||
|
|
|
@ -20,10 +20,10 @@ import { compareDate } from "helpers/date";
|
|||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { SmartList } from "components/SmartList";
|
||||
import { cIf } from "helpers/className";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -47,7 +47,7 @@ const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
|||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
|
||||
const { langui } = useLocalData();
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
|
||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ import { getOpenGraph } from "helpers/openGraph";
|
|||
import { compareDate } from "helpers/date";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { cIf } from "helpers/className";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -46,7 +46,7 @@ interface Props extends AppLayoutRequired {
|
|||
const Videos = ({ videos, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
|
||||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ import { prettyDate, prettyShortenNumber } from "helpers/formatters";
|
|||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { getVideoFile } from "helpers/videos";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -31,7 +31,7 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const { setSubPanelOpen } = useAppLayout();
|
||||
const { langui } = useLocalData();
|
||||
const router = useRouter();
|
||||
|
|
|
@ -29,11 +29,11 @@ import { getOpenGraph } from "helpers/openGraph";
|
|||
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
||||
import { getDescription } from "helpers/description";
|
||||
import { TranslatedPreviewLine } from "components/PreviewLine";
|
||||
import { useIs1ColumnLayout, useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { Ids } from "types/ids";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -45,8 +45,7 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||
const isContentPanelAtLeast2xl = useIsContentPanelAtLeast("2xl");
|
||||
const is1ColumnLayout = useIs1ColumnLayout();
|
||||
const { isContentPanelAtLeast2xl, is1ColumnLayout } = useContainerQueries();
|
||||
const { langui, languages } = useLocalData();
|
||||
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
|
|
|
@ -22,11 +22,11 @@ import { SelectiveNonNullable } from "types/SelectiveNonNullable";
|
|||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -51,7 +51,7 @@ interface Props extends AppLayoutRequired {
|
|||
const Contents = ({ contents, ...otherProps }: Props): JSX.Element => {
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const { langui } = useLocalData();
|
||||
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
|
||||
const [groupingMethod, setGroupingMethod] = useState<number>(
|
||||
DEFAULT_FILTERS_STATE.groupingMethod
|
||||
|
|
|
@ -19,10 +19,10 @@ import { TranslatedProps } from "types/TranslatedProps";
|
|||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -37,7 +37,7 @@ interface Props extends AppLayoutRequired {
|
|||
|
||||
const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
|
|
|
@ -159,7 +159,7 @@ const Editor = (props: Props): JSX.Element => {
|
|||
const contentPanel = useMemo(
|
||||
() => (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||
<Popup onClose={() => setConverterOpened(false)} state={converterOpened}>
|
||||
<Popup isVisible={converterOpened} onCloseRequest={() => setConverterOpened(false)}>
|
||||
<div className="text-center">
|
||||
<h2 className="mt-4">Convert HTML to markdown</h2>
|
||||
<p>
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
prettySlug,
|
||||
prettyURL,
|
||||
} from "helpers/formatters";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { convertMmToInch } from "helpers/numbers";
|
||||
import {
|
||||
filterDefined,
|
||||
|
@ -38,7 +38,6 @@ import {
|
|||
isDefinedAndNotEmpty,
|
||||
sortRangedContent,
|
||||
} from "helpers/others";
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
|
@ -50,11 +49,12 @@ import { getOpenGraph } from "helpers/openGraph";
|
|||
import { getDescription } from "helpers/description";
|
||||
import { useIntersectionList } from "hooks/useIntersectionList";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { useIsContentPanelNoMoreThan } from "hooks/useContainerQuery";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { Ids } from "types/ids";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { useLightBox } from "contexts/LightBoxContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -76,13 +76,13 @@ interface Props extends AppLayoutRequired {
|
|||
const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||
const { currency } = useUserSettings();
|
||||
const { langui, currencies } = useLocalData();
|
||||
const isContentPanelNoMoreThan3xl = useIsContentPanelNoMoreThan("3xl");
|
||||
const isContentPanelNoMoreThanSm = useIsContentPanelNoMoreThan("sm");
|
||||
const { isContentPanelAtLeast3xl, isContentPanelAtLeastSm } = useContainerQueries();
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const router = useRouter();
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(false);
|
||||
|
||||
const { showLightBox } = useLightBox();
|
||||
|
||||
useScrollTopOnChange(Ids.ContentPanel, [item]);
|
||||
const currentIntersection = useIntersectionList(intersectionIds);
|
||||
|
||||
|
@ -158,8 +158,6 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
const contentPanel = useMemo(
|
||||
() => (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Full}>
|
||||
<LightBox />
|
||||
|
||||
<ReturnButton
|
||||
href="/library/"
|
||||
title={langui.library}
|
||||
|
@ -170,18 +168,16 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
<div
|
||||
className={cJoin(
|
||||
"relative h-[50vh] w-full cursor-pointer drop-shadow-shade-xl",
|
||||
cIf(isContentPanelNoMoreThan3xl, "h-[60vh]", "mb-16")
|
||||
)}
|
||||
onClick={() => {
|
||||
if (item.thumbnail?.data?.attributes) {
|
||||
openLightBox([getAssetURL(item.thumbnail.data.attributes.url, ImageQuality.Large)]);
|
||||
}
|
||||
}}>
|
||||
cIf(isContentPanelAtLeast3xl, "mb-16", "h-[60vh]")
|
||||
)}>
|
||||
{item.thumbnail?.data?.attributes ? (
|
||||
<Img
|
||||
src={item.thumbnail.data.attributes}
|
||||
quality={ImageQuality.Large}
|
||||
className="h-full w-full object-contain"
|
||||
onClick={() => {
|
||||
showLightBox([item.thumbnail?.data?.attributes]);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className="aspect-[21/29.7] w-full rounded-xl bg-light"></div>
|
||||
|
@ -254,12 +250,12 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
className="relative aspect-square cursor-pointer
|
||||
transition-transform hover:scale-[1.02]"
|
||||
onClick={() => {
|
||||
const images: string[] = filterHasAttributes(item.gallery?.data, [
|
||||
"attributes",
|
||||
] as const).map((image) =>
|
||||
getAssetURL(image.attributes.url, ImageQuality.Large)
|
||||
showLightBox(
|
||||
filterHasAttributes(item.gallery?.data, ["attributes"] as const).map(
|
||||
(image) => image.attributes
|
||||
),
|
||||
index
|
||||
);
|
||||
openLightBox(images, index);
|
||||
}}>
|
||||
<Img
|
||||
className="h-full w-full rounded-lg
|
||||
|
@ -280,7 +276,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
<div
|
||||
className={cJoin(
|
||||
"grid place-items-center gap-y-8",
|
||||
cIf(!isContentPanelNoMoreThan3xl, "grid-flow-col place-content-between")
|
||||
cIf(isContentPanelAtLeast3xl, "grid-flow-col place-content-between")
|
||||
)}>
|
||||
{item.metadata?.[0] && (
|
||||
<div className="grid place-content-start place-items-center">
|
||||
|
@ -337,25 +333,25 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
<div
|
||||
className={cJoin(
|
||||
"grid gap-4",
|
||||
cIf(isContentPanelNoMoreThan3xl, "place-items-center")
|
||||
cIf(!isContentPanelAtLeast3xl, "place-items-center")
|
||||
)}>
|
||||
<h3 className="text-xl">{langui.size}</h3>
|
||||
<div
|
||||
className={cJoin(
|
||||
"grid w-full",
|
||||
cIf(
|
||||
isContentPanelNoMoreThanSm,
|
||||
"grid-flow-row place-content-center gap-8",
|
||||
"grid-flow-col place-content-between"
|
||||
isContentPanelAtLeastSm,
|
||||
"grid-flow-col place-content-between",
|
||||
"grid-flow-row place-content-center gap-8"
|
||||
)
|
||||
)}>
|
||||
<div
|
||||
className={cJoin(
|
||||
"grid gap-x-4",
|
||||
cIf(
|
||||
isContentPanelNoMoreThan3xl,
|
||||
"place-items-center",
|
||||
"grid-flow-col place-items-start"
|
||||
isContentPanelAtLeast3xl,
|
||||
"grid-flow-col place-items-start",
|
||||
"place-items-center"
|
||||
)
|
||||
)}>
|
||||
<p className="font-bold">{langui.width}:</p>
|
||||
|
@ -368,9 +364,9 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
className={cJoin(
|
||||
"grid gap-x-4",
|
||||
cIf(
|
||||
isContentPanelNoMoreThan3xl,
|
||||
"place-items-center",
|
||||
"grid-flow-col place-items-start"
|
||||
isContentPanelAtLeast3xl,
|
||||
"grid-flow-col place-items-start",
|
||||
"place-items-center"
|
||||
)
|
||||
)}>
|
||||
<p className="font-bold">{langui.height}:</p>
|
||||
|
@ -384,9 +380,9 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
className={cJoin(
|
||||
"grid gap-x-4",
|
||||
cIf(
|
||||
isContentPanelNoMoreThan3xl,
|
||||
"place-items-center",
|
||||
"grid-flow-col place-items-start"
|
||||
isContentPanelAtLeast3xl,
|
||||
"grid-flow-col place-items-start",
|
||||
"place-items-center"
|
||||
)
|
||||
)}>
|
||||
<p className="font-bold">{langui.thickness}:</p>
|
||||
|
@ -405,7 +401,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
<div
|
||||
className={cJoin(
|
||||
"grid gap-4",
|
||||
cIf(isContentPanelNoMoreThan3xl, "place-items-center")
|
||||
cIf(!isContentPanelAtLeast3xl, "place-items-center")
|
||||
)}>
|
||||
<h3 className="text-xl">{langui.type_information}</h3>
|
||||
<div className="flex flex-wrap place-content-between gap-x-8">
|
||||
|
@ -555,7 +551,7 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
isDefined(rangedContent.attributes.scan_set) &&
|
||||
rangedContent.attributes.scan_set.length > 0
|
||||
}
|
||||
condensed={isContentPanelNoMoreThan3xl}
|
||||
condensed={!isContentPanelAtLeast3xl}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
|
@ -566,9 +562,8 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
</ContentPanel>
|
||||
),
|
||||
[
|
||||
LightBox,
|
||||
langui,
|
||||
isContentPanelNoMoreThan3xl,
|
||||
isContentPanelAtLeast3xl,
|
||||
item.thumbnail?.data?.attributes,
|
||||
item.subitem_of?.data,
|
||||
item.title,
|
||||
|
@ -588,13 +583,13 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
|||
router.locale,
|
||||
currencies,
|
||||
currency,
|
||||
isContentPanelNoMoreThanSm,
|
||||
isContentPanelAtLeastSm,
|
||||
isVariantSet,
|
||||
hoverable,
|
||||
toggleKeepInfoVisible,
|
||||
keepInfoVisible,
|
||||
displayOpenScans,
|
||||
openLightBox,
|
||||
showLightBox,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { GetStaticPaths, GetStaticPathsResult, GetStaticProps } from "next";
|
||||
import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import Hotkeys from "react-hot-keys";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import Slider from "rc-slider";
|
||||
import { useRouter } from "next/router";
|
||||
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
|
||||
|
@ -23,7 +23,6 @@ import { getLangui } from "graphql/fetchLocalData";
|
|||
import { ContentPanel, ContentPanelWidthSizes } from "components/Panels/ContentPanel";
|
||||
import { Img } from "components/Img";
|
||||
import { getAssetFilename, ImageQuality } from "helpers/img";
|
||||
import { useIs1ColumnLayout, useIsContentPanelNoMoreThan } from "hooks/useContainerQuery";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { clamp, isInteger } from "helpers/numbers";
|
||||
import { SubPanel } from "components/Panels/SubPanel";
|
||||
|
@ -45,6 +44,7 @@ import { useFullscreen } from "hooks/useFullscreen";
|
|||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
const CUSTOM_DARK_DROPSHADOW = `
|
||||
drop-shadow(0 0 0.5em rgb(var(--theme-color-shade) / 30%))
|
||||
|
@ -90,7 +90,7 @@ const LibrarySlug = ({
|
|||
item,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const is1ColumnLayout = useIs1ColumnLayout();
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const { darkMode } = useUserSettings();
|
||||
const {
|
||||
|
@ -114,7 +114,7 @@ const LibrarySlug = ({
|
|||
);
|
||||
const router = useRouter();
|
||||
|
||||
const { isFullscreen, toggleFullscreen } = useFullscreen(Ids.ContentPanel);
|
||||
const { isFullscreen, toggleFullscreen, requestFullscreen } = useFullscreen(Ids.ContentPanel);
|
||||
|
||||
const effectiveDisplayMode = useMemo(
|
||||
() =>
|
||||
|
@ -173,6 +173,19 @@ const LibrarySlug = ({
|
|||
[changeCurrentPageIndex, effectiveDisplayMode, pageOrder]
|
||||
);
|
||||
|
||||
useHotkeys("left", () => handlePageNavigation("left"), { enabled: !isGalleryMode }, [
|
||||
handlePageNavigation,
|
||||
]);
|
||||
|
||||
useHotkeys("up", () => setIsGalleryMode(true), { enabled: !isGalleryMode }, [setIsGalleryMode]);
|
||||
useHotkeys("down", () => setIsGalleryMode(false), { enabled: isGalleryMode }, [setIsGalleryMode]);
|
||||
|
||||
useHotkeys("f", () => requestFullscreen(), { enabled: !isFullscreen }, [requestFullscreen]);
|
||||
|
||||
useHotkeys("right", () => handlePageNavigation("right"), { enabled: !isGalleryMode }, [
|
||||
handlePageNavigation,
|
||||
]);
|
||||
|
||||
const firstPage = useMemo(
|
||||
() =>
|
||||
pages[
|
||||
|
@ -426,12 +439,6 @@ const LibrarySlug = ({
|
|||
() => (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Full} className="grid place-content-center !p-0">
|
||||
<div className={cJoin("mb-12 grid", cIf(is1ColumnLayout, "!p-0", "!p-8"))}>
|
||||
<Hotkeys
|
||||
keyName="left,right"
|
||||
allowRepeat
|
||||
onKeyDown={(keyName) => {
|
||||
handlePageNavigation(keyName as "left" | "right");
|
||||
}}>
|
||||
<TransformWrapper
|
||||
onZoom={(zoom) => setCurrentZoom(zoom.state.scale)}
|
||||
panning={{ disabled: currentZoom <= 1, velocityDisabled: false }}
|
||||
|
@ -540,7 +547,6 @@ const LibrarySlug = ({
|
|||
)}
|
||||
</TransformComponent>
|
||||
</TransformWrapper>
|
||||
</Hotkeys>
|
||||
</div>
|
||||
<div
|
||||
className={cJoin(
|
||||
|
@ -883,7 +889,7 @@ interface ScanSetProps {
|
|||
}
|
||||
|
||||
const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps): JSX.Element => {
|
||||
const is1ColumnLayout = useIsContentPanelNoMoreThan("2xl");
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const [selectedScan, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: scanSet,
|
||||
|
@ -1028,7 +1034,7 @@ const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps):
|
|||
|
||||
<div
|
||||
className={cJoin(
|
||||
`grid items-end gap-8 border-b-[3px] border-dotted pb-12
|
||||
`grid items-end gap-8 border-b-2 border-dotted pb-12
|
||||
last-of-type:border-0`,
|
||||
cIf(
|
||||
is1ColumnLayout,
|
||||
|
|
|
@ -28,12 +28,12 @@ import { SelectiveNonNullable } from "types/SelectiveNonNullable";
|
|||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { compareDate } from "helpers/date";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -64,7 +64,7 @@ const Library = ({ items, ...otherProps }: Props): JSX.Element => {
|
|||
const hoverable = useDeviceSupportsHover();
|
||||
const { langui, currencies } = useLocalData();
|
||||
const { libraryItemUserStatus } = useLibraryItemUserStatus();
|
||||
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
|
||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||
|
||||
|
|
|
@ -21,12 +21,12 @@ import { compareDate } from "helpers/date";
|
|||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { cIf } from "helpers/className";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -48,7 +48,7 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const News = ({ posts, ...otherProps }: Props): JSX.Element => {
|
||||
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||
|
|
|
@ -14,18 +14,18 @@ import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/ot
|
|||
import { WikiPageWithTranslations } from "types/types";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { prettySlug, sJoin } from "helpers/formatters";
|
||||
import { useLightBox } from "hooks/useLightBox";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
||||
import { getDescription } from "helpers/description";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useIs3ColumnsLayout } from "hooks/useContainerQuery";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { prettyTerminalBoxedTitle, prettyTerminalUnderlinedTitle } from "helpers/terminal";
|
||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { useLightBox } from "contexts/LightBoxContext";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -40,6 +40,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
|||
const { langui } = useLocalData();
|
||||
const router = useRouter();
|
||||
const isTerminalMode = useIsTerminalMode();
|
||||
const { showLightBox } = useLightBox();
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: page.translations,
|
||||
languageExtractor: useCallback(
|
||||
|
@ -48,9 +49,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
|||
[]
|
||||
),
|
||||
});
|
||||
|
||||
const [openLightBox, LightBox] = useLightBox();
|
||||
const is3ColumnsLayout = useIs3ColumnsLayout();
|
||||
const { is3ColumnsLayout } = useContainerQueries();
|
||||
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
|
@ -64,8 +63,6 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
|||
const contentPanel = useMemo(
|
||||
() => (
|
||||
<ContentPanel width={ContentPanelWidthSizes.Large}>
|
||||
<LightBox />
|
||||
|
||||
<ReturnButton
|
||||
href={`/wiki`}
|
||||
title={langui.wiki}
|
||||
|
@ -98,10 +95,8 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
|||
quality={ImageQuality.Medium}
|
||||
className="w-full cursor-pointer"
|
||||
onClick={() => {
|
||||
if (page.thumbnail?.data?.attributes?.url) {
|
||||
openLightBox([
|
||||
getAssetURL(page.thumbnail.data.attributes.url, ImageQuality.Large),
|
||||
]);
|
||||
if (page.thumbnail?.data?.attributes) {
|
||||
showLightBox([page.thumbnail.data.attributes]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -183,16 +178,18 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
|||
),
|
||||
[
|
||||
LanguageSwitcher,
|
||||
LightBox,
|
||||
is3ColumnsLayout,
|
||||
languageSwitcherProps,
|
||||
langui,
|
||||
openLightBox,
|
||||
langui.categories,
|
||||
langui.summary,
|
||||
langui.tags,
|
||||
langui.wiki,
|
||||
page.categories?.data,
|
||||
page.definitions,
|
||||
page.tags?.data,
|
||||
page.thumbnail?.data?.attributes,
|
||||
selectedTranslation,
|
||||
showLightBox,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -22,13 +22,13 @@ import { SelectiveNonNullable } from "types/SelectiveNonNullable";
|
|||
import { prettySlug } from "helpers/formatters";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||
import { useIsContentPanelAtLeast } from "hooks/useContainerQuery";
|
||||
import { cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -53,7 +53,7 @@ interface Props extends AppLayoutRequired {
|
|||
const Wiki = ({ pages, ...otherProps }: Props): JSX.Element => {
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const { langui } = useLocalData();
|
||||
const isContentPanelAtLeast4xl = useIsContentPanelAtLeast("4xl");
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const isTerminalMode = useIsTerminalMode();
|
||||
|
||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
.animation-carret {
|
||||
animation-name: blink;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: step-end;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.animate-zoom-in {
|
||||
animation-name: zoom-in;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
@keyframes zoom-in {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
from,
|
||||
to {
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
50% {
|
||||
border-bottom-style: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
.texture-paper-dots {
|
||||
@apply bg-[length:10cm] bg-local [background-image:var(--theme-texture-dots)]
|
||||
[background-blend-mode:var(--theme-texture-dots-blend)];
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
.false {
|
||||
@apply border-2 border-[red] text-[red] outline-dotted outline-2 outline-[red];
|
||||
}
|
||||
|
||||
.undefined {
|
||||
@apply border-2 border-[green] text-[green] outline-dotted outline-2 outline-[green];
|
||||
}
|
||||
|
||||
.null {
|
||||
@apply border-2 border-[blue] text-[blue] outline-dotted outline-2 outline-[blue];
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
.formatted h1,
|
||||
.formatted h2,
|
||||
.formatted h3,
|
||||
.formatted h4,
|
||||
.formatted h5,
|
||||
.formatted h6 {
|
||||
@apply flex justify-center gap-3 text-center;
|
||||
}
|
||||
|
||||
.formatted h1 {
|
||||
@apply my-16 text-4xl;
|
||||
}
|
||||
|
||||
.formatted h1 + h2 {
|
||||
@apply -mt-10;
|
||||
}
|
||||
|
||||
.formatted h2 {
|
||||
@apply my-12 text-3xl;
|
||||
}
|
||||
|
||||
.formatted h2 + h3 {
|
||||
@apply -mt-8;
|
||||
}
|
||||
|
||||
.formatted h3 {
|
||||
@apply my-8 text-2xl;
|
||||
}
|
||||
|
||||
.formatted h3 + h4 {
|
||||
@apply -mt-6;
|
||||
}
|
||||
|
||||
.formatted h4 {
|
||||
@apply my-6 text-xl;
|
||||
}
|
||||
|
||||
.formatted h5 {
|
||||
@apply my-4 text-lg;
|
||||
}
|
||||
|
||||
.formatted p,
|
||||
.formatted strong {
|
||||
@apply my-2 text-justify;
|
||||
}
|
||||
|
||||
.formatted strong {
|
||||
@apply font-black;
|
||||
}
|
||||
|
||||
.formatted footer {
|
||||
@apply border-t-2 border-dotted pt-6;
|
||||
}
|
||||
|
||||
.formatted footer > div {
|
||||
@apply my-2 rounded-xl px-6 py-4;
|
||||
}
|
||||
|
||||
.formatted footer > div:target {
|
||||
@apply bg-mid shadow-inner-sm shadow-shade;
|
||||
}
|
||||
|
||||
.formatted li::marker {
|
||||
@apply text-dark;
|
||||
}
|
||||
|
||||
.formatted code {
|
||||
@apply font-mono;
|
||||
}
|
||||
|
||||
.formatted pre > code {
|
||||
@apply block whitespace-pre-line;
|
||||
}
|
||||
|
||||
.formatted ul {
|
||||
@apply list-disc pl-4;
|
||||
}
|
||||
|
||||
.formatted ol {
|
||||
@apply list-decimal pl-4;
|
||||
}
|
||||
|
||||
.formatted blockquote {
|
||||
@apply my-8 rounded-lg border-2 border-mid p-5 text-center;
|
||||
}
|
||||
|
||||
.formatted blockquote cite {
|
||||
@apply block text-dark;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
#__next {
|
||||
@apply bg-light font-body font-medium text-black;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply box-border scroll-m-[40vh] scroll-smooth ![-webkit-tap-highlight-color:transparent];
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@apply font-headers font-black;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply cursor-pointer underline decoration-dark decoration-dotted
|
||||
underline-offset-2 transition-colors hover:text-dark;
|
||||
}
|
||||
|
||||
*::selection {
|
||||
@apply bg-dark text-light;
|
||||
}
|
||||
|
||||
mark {
|
||||
@apply bg-mid px-2;
|
||||
}
|
||||
|
||||
/* SCROLLBARS STYLING */
|
||||
|
||||
* {
|
||||
@apply [scrollbar-color:theme(colors.dark/1)_transparent] [scrollbar-width:thin];
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
@apply w-3;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
@apply bg-light;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
@apply rounded-full border-2 border-solid border-light bg-dark;
|
||||
}
|
||||
|
||||
/* INPUT */
|
||||
|
||||
input,
|
||||
textarea {
|
||||
@apply rounded-full bg-light p-2 text-center text-dark outline outline-1 -outline-offset-1
|
||||
outline-mid transition-all placeholder:text-dark placeholder:opacity-60 hover:bg-mid
|
||||
hover:outline-transparent;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
@apply text-dark opacity-60;
|
||||
}
|
||||
|
||||
input:focus-visible,
|
||||
textarea:focus-within {
|
||||
@apply bg-mid shadow-inner-sm shadow-shade outline-none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@apply rounded-2xl p-6 text-left;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
@apply grid cursor-pointer place-content-center place-items-center rounded-full border
|
||||
border-dark px-4 pt-[0.4rem] pb-[0.5rem] text-dark outline-none transition-all hover:bg-dark
|
||||
hover:text-light hover:drop-shadow-shade-lg active:border-black active:bg-black
|
||||
active:text-light active:drop-shadow-black-lg;
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
.rc-slider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 14px;
|
||||
padding: 5px 0;
|
||||
border-radius: 6px;
|
||||
touch-action: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.rc-slider * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.rc-slider-rail {
|
||||
@apply h-2 rounded-full bg-mid/80;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
.rc-slider-track {
|
||||
@apply h-2 rounded-full bg-mid bg-dark/20 shadow-inner-sm shadow-shade;
|
||||
position: absolute;
|
||||
}
|
||||
.rc-slider-handle {
|
||||
@apply -mt-1 h-4 w-4 rounded-full bg-dark transition-shadow;
|
||||
position: absolute;
|
||||
cursor: grab;
|
||||
touch-action: pan-x;
|
||||
}
|
||||
.rc-slider-handle-dragging.rc-slider-handle-dragging.rc-slider-handle-dragging {
|
||||
@apply shadow-sm shadow-shade;
|
||||
}
|
||||
|
||||
.rc-slider-mark {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
}
|
||||
.rc-slider-mark-text {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rc-slider-mark-text-active {
|
||||
color: #666;
|
||||
}
|
||||
.rc-slider-step {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
.rc-slider-dot {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
vertical-align: middle;
|
||||
background-color: #fff;
|
||||
border: 2px solid #e9e9e9;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rc-slider-dot-active {
|
||||
border-color: #96dbfa;
|
||||
}
|
||||
.rc-slider-dot-reverse {
|
||||
margin-right: -4px;
|
||||
}
|
||||
.rc-slider-disabled {
|
||||
background-color: #e9e9e9;
|
||||
}
|
||||
.rc-slider-disabled .rc-slider-track {
|
||||
background-color: #ccc;
|
||||
}
|
||||
.rc-slider-disabled .rc-slider-handle,
|
||||
.rc-slider-disabled .rc-slider-dot {
|
||||
background-color: #fff;
|
||||
border-color: #ccc;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.rc-slider-disabled .rc-slider-mark-text,
|
||||
.rc-slider-disabled .rc-slider-dot {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
.rc-slider-vertical {
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-rail {
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-track {
|
||||
bottom: 0;
|
||||
left: 5px;
|
||||
width: 4px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-handle {
|
||||
margin-top: 0;
|
||||
margin-left: -5px;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-mark {
|
||||
top: 0;
|
||||
left: 18px;
|
||||
height: 100%;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-step {
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-dot {
|
||||
margin-left: -2px;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter,
|
||||
.rc-slider-tooltip-zoom-down-appear {
|
||||
display: block !important;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave {
|
||||
display: block !important;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter.rc-slider-tooltip-zoom-down-enter-active,
|
||||
.rc-slider-tooltip-zoom-down-appear.rc-slider-tooltip-zoom-down-appear-active {
|
||||
animation-name: rcSliderTooltipZoomDownIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave.rc-slider-tooltip-zoom-down-leave-active {
|
||||
animation-name: rcSliderTooltipZoomDownOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter,
|
||||
.rc-slider-tooltip-zoom-down-appear {
|
||||
transform: scale(0, 0);
|
||||
animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave {
|
||||
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
}
|
||||
@keyframes rcSliderTooltipZoomDownIn {
|
||||
0% {
|
||||
transform: scale(0, 0);
|
||||
transform-origin: 50% 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1, 1);
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
}
|
||||
@keyframes rcSliderTooltipZoomDownOut {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0, 0);
|
||||
transform-origin: 50% 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.rc-slider-tooltip {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
visibility: visible;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider-tooltip * {
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider-tooltip-hidden {
|
||||
display: none;
|
||||
}
|
||||
.rc-slider-tooltip-placement-top {
|
||||
padding: 4px 0 8px 0;
|
||||
}
|
||||
.rc-slider-tooltip-inner {
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
padding: 6px 2px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background-color: #6c6c6c;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 4px #d9d9d9;
|
||||
}
|
||||
.rc-slider-tooltip-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.rc-slider-tooltip-placement-top .rc-slider-tooltip-arrow {
|
||||
bottom: 4px;
|
||||
left: 50%;
|
||||
margin-left: -4px;
|
||||
border-width: 4px 4px 0;
|
||||
border-top-color: #6c6c6c;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
.tippy-box[data-animation="fade"][data-state="hidden"] {
|
||||
@apply opacity-0;
|
||||
}
|
||||
[data-tippy-root] {
|
||||
max-width: calc(100vw - 10px);
|
||||
}
|
||||
.tippy-box {
|
||||
@apply relative rounded-lg bg-light transition-[transform,visibility,opacity]
|
||||
drop-shadow-shade-xl;
|
||||
}
|
||||
.tippy-box[data-placement^="top"] > .tippy-arrow {
|
||||
@apply bottom-0;
|
||||
}
|
||||
.tippy-box[data-placement^="top"] > .tippy-arrow:before {
|
||||
bottom: -7px;
|
||||
left: 0;
|
||||
border-width: 8px 8px 0;
|
||||
border-top-color: initial;
|
||||
transform-origin: center top;
|
||||
}
|
||||
.tippy-box[data-placement^="bottom"] > .tippy-arrow {
|
||||
top: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="bottom"] > .tippy-arrow:before {
|
||||
top: -7px;
|
||||
left: 0;
|
||||
border-width: 0 8px 8px;
|
||||
border-bottom-color: initial;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
.tippy-box[data-placement^="left"] > .tippy-arrow {
|
||||
right: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="left"] > .tippy-arrow:before {
|
||||
border-width: 8px 0 8px 8px;
|
||||
border-left-color: initial;
|
||||
right: -7px;
|
||||
transform-origin: center left;
|
||||
}
|
||||
.tippy-box[data-placement^="right"] > .tippy-arrow {
|
||||
left: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="right"] > .tippy-arrow:before {
|
||||
left: -7px;
|
||||
border-width: 8px 8px 8px 0;
|
||||
border-right-color: initial;
|
||||
transform-origin: center right;
|
||||
}
|
||||
.tippy-box[data-inertia][data-state="visible"] {
|
||||
transition-timing-function: cubic-bezier(0.54, 1.5, 0.38, 1.11);
|
||||
}
|
||||
.tippy-arrow {
|
||||
@apply h-4 w-4 text-light;
|
||||
}
|
||||
.tippy-arrow:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.tippy-content {
|
||||
@apply relative z-10 px-6 py-4;
|
||||
}
|
510
src/tailwind.css
|
@ -1,510 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
* {
|
||||
@apply box-border scroll-m-[40vh] scroll-smooth font-body font-medium;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@apply font-headers font-black;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply cursor-pointer underline decoration-dark decoration-dotted
|
||||
underline-offset-2 transition-colors hover:text-dark;
|
||||
}
|
||||
|
||||
*::selection {
|
||||
@apply bg-dark text-light;
|
||||
}
|
||||
|
||||
mark {
|
||||
@apply bg-mid px-2 text-black;
|
||||
}
|
||||
|
||||
/* SCROLLBARS STYLING */
|
||||
|
||||
* {
|
||||
@apply [scrollbar-color:theme(colors.dark/1)_transparent] [scrollbar-width:thin];
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
@apply w-3;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
@apply bg-light;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
@apply rounded-full border-[3px] border-solid border-light bg-dark;
|
||||
}
|
||||
|
||||
/* CHANGE FORMATTED DEFAULTS */
|
||||
|
||||
.formatted h1,
|
||||
.formatted h2,
|
||||
.formatted h3,
|
||||
.formatted h4,
|
||||
.formatted h5,
|
||||
.formatted h6 {
|
||||
@apply flex justify-center gap-3 text-center;
|
||||
}
|
||||
|
||||
.formatted h1 {
|
||||
@apply my-16 text-4xl;
|
||||
}
|
||||
|
||||
.formatted h1 + h2 {
|
||||
@apply -mt-10;
|
||||
}
|
||||
|
||||
.formatted h2 {
|
||||
@apply my-12 text-3xl;
|
||||
}
|
||||
|
||||
.formatted h2 + h3 {
|
||||
@apply -mt-8;
|
||||
}
|
||||
|
||||
.formatted h3 {
|
||||
@apply my-8 text-2xl;
|
||||
}
|
||||
|
||||
.formatted h3 + h4 {
|
||||
@apply -mt-6;
|
||||
}
|
||||
|
||||
.formatted h4 {
|
||||
@apply my-6 text-xl;
|
||||
}
|
||||
|
||||
.formatted h5 {
|
||||
@apply my-4 text-lg;
|
||||
}
|
||||
|
||||
.formatted p,
|
||||
.formatted strong {
|
||||
@apply my-2 text-justify;
|
||||
}
|
||||
|
||||
.formatted strong {
|
||||
@apply font-black;
|
||||
}
|
||||
|
||||
.formatted footer {
|
||||
@apply border-t-[3px] border-dotted pt-6;
|
||||
}
|
||||
|
||||
.formatted footer > div {
|
||||
@apply my-2 rounded-xl px-6 py-4;
|
||||
}
|
||||
|
||||
.formatted footer > div:target {
|
||||
@apply bg-mid shadow-inner-sm shadow-shade;
|
||||
}
|
||||
|
||||
.formatted li::marker {
|
||||
@apply text-dark;
|
||||
}
|
||||
|
||||
.formatted code {
|
||||
@apply font-mono;
|
||||
}
|
||||
|
||||
.formatted pre > code {
|
||||
@apply block whitespace-pre-line;
|
||||
}
|
||||
|
||||
.formatted ul {
|
||||
@apply list-disc pl-4;
|
||||
}
|
||||
|
||||
.formatted ol {
|
||||
@apply list-decimal pl-4;
|
||||
}
|
||||
|
||||
.formatted blockquote {
|
||||
@apply my-8 rounded-lg border-2 border-mid p-5 text-center;
|
||||
}
|
||||
|
||||
.formatted blockquote cite {
|
||||
@apply block text-dark;
|
||||
}
|
||||
|
||||
/* INPUT */
|
||||
|
||||
input,
|
||||
textarea {
|
||||
@apply rounded-full bg-light p-2 text-center text-dark outline outline-2 outline-offset-[-2px]
|
||||
outline-mid transition-all placeholder:text-dark placeholder:opacity-60 hover:bg-mid
|
||||
hover:outline-[transparent];
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
@apply text-dark opacity-60;
|
||||
}
|
||||
|
||||
input:focus-visible,
|
||||
textarea:focus-within {
|
||||
@apply bg-mid shadow-inner-sm shadow-shade outline-none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@apply rounded-2xl p-6 text-left;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
@apply grid cursor-pointer place-content-center place-items-center rounded-full border-[1px]
|
||||
border-dark px-4 pt-[0.4rem] pb-[0.5rem] text-dark outline-none transition-all hover:bg-dark
|
||||
hover:text-light hover:drop-shadow-shade-lg active:border-black active:bg-black
|
||||
active:text-light active:drop-shadow-black-lg;
|
||||
}
|
||||
|
||||
.texture-paper-dots {
|
||||
@apply bg-[length:10cm] bg-local [background-image:var(--theme-texture-dots)]
|
||||
[background-blend-mode:var(--theme-texture-dots-blend)];
|
||||
}
|
||||
|
||||
/* ANIMATION */
|
||||
|
||||
.animation-carret {
|
||||
animation-name: blink;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: step-end;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.animate-zoom-in {
|
||||
animation-name: zoom-in;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: ease-in-out;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
@keyframes zoom-in {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
from,
|
||||
to {
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
50% {
|
||||
border-bottom-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* DEBUGGING */
|
||||
.false {
|
||||
@apply border-2 border-[red] text-[red] outline-dotted outline-2 outline-[red];
|
||||
}
|
||||
|
||||
.undefined {
|
||||
@apply border-2 border-[green] text-[green] outline-dotted outline-2 outline-[green];
|
||||
}
|
||||
|
||||
.null {
|
||||
@apply border-2 border-[blue] text-[blue] outline-dotted outline-2 outline-[blue];
|
||||
}
|
||||
|
||||
/* TIPPY */
|
||||
|
||||
.tippy-box[data-animation="fade"][data-state="hidden"] {
|
||||
@apply opacity-0;
|
||||
}
|
||||
[data-tippy-root] {
|
||||
max-width: calc(100vw - 10px);
|
||||
}
|
||||
.tippy-box {
|
||||
@apply relative rounded-lg bg-light transition-[transform,_visibility,_opacity]
|
||||
drop-shadow-shade-xl;
|
||||
}
|
||||
.tippy-box[data-placement^="top"] > .tippy-arrow {
|
||||
@apply bottom-0;
|
||||
}
|
||||
.tippy-box[data-placement^="top"] > .tippy-arrow:before {
|
||||
bottom: -7px;
|
||||
left: 0;
|
||||
border-width: 8px 8px 0;
|
||||
border-top-color: initial;
|
||||
transform-origin: center top;
|
||||
}
|
||||
.tippy-box[data-placement^="bottom"] > .tippy-arrow {
|
||||
top: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="bottom"] > .tippy-arrow:before {
|
||||
top: -7px;
|
||||
left: 0;
|
||||
border-width: 0 8px 8px;
|
||||
border-bottom-color: initial;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
.tippy-box[data-placement^="left"] > .tippy-arrow {
|
||||
right: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="left"] > .tippy-arrow:before {
|
||||
border-width: 8px 0 8px 8px;
|
||||
border-left-color: initial;
|
||||
right: -7px;
|
||||
transform-origin: center left;
|
||||
}
|
||||
.tippy-box[data-placement^="right"] > .tippy-arrow {
|
||||
left: 0;
|
||||
}
|
||||
.tippy-box[data-placement^="right"] > .tippy-arrow:before {
|
||||
left: -7px;
|
||||
border-width: 8px 8px 8px 0;
|
||||
border-right-color: initial;
|
||||
transform-origin: center right;
|
||||
}
|
||||
.tippy-box[data-inertia][data-state="visible"] {
|
||||
transition-timing-function: cubic-bezier(0.54, 1.5, 0.38, 1.11);
|
||||
}
|
||||
.tippy-arrow {
|
||||
@apply h-4 w-4 text-light;
|
||||
}
|
||||
.tippy-arrow:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.tippy-content {
|
||||
@apply relative z-10 px-6 py-4;
|
||||
}
|
||||
|
||||
/* RC SLIDER */
|
||||
|
||||
.rc-slider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 14px;
|
||||
padding: 5px 0;
|
||||
border-radius: 6px;
|
||||
touch-action: none;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider * {
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider-rail {
|
||||
@apply h-2 rounded-full bg-mid/80;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
.rc-slider-track {
|
||||
@apply h-2 rounded-full bg-mid bg-dark/20 shadow-inner-sm shadow-shade;
|
||||
position: absolute;
|
||||
}
|
||||
.rc-slider-handle {
|
||||
@apply -mt-1 h-4 w-4 rounded-full bg-dark transition-shadow;
|
||||
position: absolute;
|
||||
cursor: grab;
|
||||
touch-action: pan-x;
|
||||
}
|
||||
.rc-slider-handle-dragging.rc-slider-handle-dragging.rc-slider-handle-dragging {
|
||||
@apply shadow-sm shadow-shade;
|
||||
}
|
||||
|
||||
.rc-slider-mark {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
}
|
||||
.rc-slider-mark-text {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rc-slider-mark-text-active {
|
||||
color: #666;
|
||||
}
|
||||
.rc-slider-step {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
.rc-slider-dot {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
vertical-align: middle;
|
||||
background-color: #fff;
|
||||
border: 2px solid #e9e9e9;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
.rc-slider-dot-active {
|
||||
border-color: #96dbfa;
|
||||
}
|
||||
.rc-slider-dot-reverse {
|
||||
margin-right: -4px;
|
||||
}
|
||||
.rc-slider-disabled {
|
||||
background-color: #e9e9e9;
|
||||
}
|
||||
.rc-slider-disabled .rc-slider-track {
|
||||
background-color: #ccc;
|
||||
}
|
||||
.rc-slider-disabled .rc-slider-handle,
|
||||
.rc-slider-disabled .rc-slider-dot {
|
||||
background-color: #fff;
|
||||
border-color: #ccc;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.rc-slider-disabled .rc-slider-mark-text,
|
||||
.rc-slider-disabled .rc-slider-dot {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
.rc-slider-vertical {
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-rail {
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-track {
|
||||
bottom: 0;
|
||||
left: 5px;
|
||||
width: 4px;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-handle {
|
||||
margin-top: 0;
|
||||
margin-left: -5px;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-mark {
|
||||
top: 0;
|
||||
left: 18px;
|
||||
height: 100%;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-step {
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
}
|
||||
.rc-slider-vertical .rc-slider-dot {
|
||||
margin-left: -2px;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter,
|
||||
.rc-slider-tooltip-zoom-down-appear {
|
||||
display: block !important;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave {
|
||||
display: block !important;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter.rc-slider-tooltip-zoom-down-enter-active,
|
||||
.rc-slider-tooltip-zoom-down-appear.rc-slider-tooltip-zoom-down-appear-active {
|
||||
animation-name: rcSliderTooltipZoomDownIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave.rc-slider-tooltip-zoom-down-leave-active {
|
||||
animation-name: rcSliderTooltipZoomDownOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-enter,
|
||||
.rc-slider-tooltip-zoom-down-appear {
|
||||
transform: scale(0, 0);
|
||||
animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
.rc-slider-tooltip-zoom-down-leave {
|
||||
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
|
||||
}
|
||||
@keyframes rcSliderTooltipZoomDownIn {
|
||||
0% {
|
||||
transform: scale(0, 0);
|
||||
transform-origin: 50% 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1, 1);
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
}
|
||||
@keyframes rcSliderTooltipZoomDownOut {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
100% {
|
||||
transform: scale(0, 0);
|
||||
transform-origin: 50% 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.rc-slider-tooltip {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
visibility: visible;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider-tooltip * {
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.rc-slider-tooltip-hidden {
|
||||
display: none;
|
||||
}
|
||||
.rc-slider-tooltip-placement-top {
|
||||
padding: 4px 0 8px 0;
|
||||
}
|
||||
.rc-slider-tooltip-inner {
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
padding: 6px 2px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background-color: #6c6c6c;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 4px #d9d9d9;
|
||||
}
|
||||
.rc-slider-tooltip-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
.rc-slider-tooltip-placement-top .rc-slider-tooltip-arrow {
|
||||
bottom: 4px;
|
||||
left: 50%;
|
||||
margin-left: -4px;
|
||||
border-width: 4px 4px 0;
|
||||
border-top-color: #6c6c6c;
|
||||
}
|
|
@ -2,4 +2,5 @@ export enum Ids {
|
|||
Body = "bodyqs65d4a98d56az48z64d",
|
||||
ContentPanel = "contentPanel495922447721572",
|
||||
SubPanel = "subPanelz9e8rs2d3f18zer98ze",
|
||||
LightBox = "lightBoxqsd564az89e732s1",
|
||||
}
|
||||
|
|
|
@ -27,10 +27,43 @@ module.exports = {
|
|||
hoverable: { raw: "(hover: hover)" },
|
||||
notHoverable: { raw: "(hover: none)" },
|
||||
},
|
||||
backdropBlur: {
|
||||
none: "0",
|
||||
sm: "0.1rem",
|
||||
DEFAULT: "0.2rem",
|
||||
md: "0.5rem",
|
||||
lg: "1rem",
|
||||
},
|
||||
borderWidth: {
|
||||
0: "0",
|
||||
2: "0.2rem",
|
||||
4: "0.4rem",
|
||||
8: "0.8rem",
|
||||
DEFAULT: "0.1rem",
|
||||
},
|
||||
outlineWidth: {
|
||||
0: "0",
|
||||
1: "0.15rem",
|
||||
2: "0.17rem",
|
||||
},
|
||||
outlineOffset: {
|
||||
0: "0",
|
||||
1: "0.15rem",
|
||||
2: "0.17rem",
|
||||
},
|
||||
extend: {
|
||||
boxShadow: {
|
||||
"inner-sm": "inset 0 1px 4px -2px",
|
||||
},
|
||||
transitionProperty: {
|
||||
height: "height, max-height, min-height",
|
||||
filter: "filter, backdrop-filter",
|
||||
colors:
|
||||
"color, background-color, border-color, text-decoration-color, fill, stroke, outline-color",
|
||||
},
|
||||
outlineColor: {
|
||||
transparent: "transparent",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
|
|