Added darkmode and replaced redux by context

This commit is contained in:
DrMint 2022-02-22 13:53:44 +01:00
parent a39313c655
commit c4ce6aa11e
25 changed files with 362 additions and 560 deletions

View File

@ -22,5 +22,6 @@
- Support for [Material Icons](https://fonts.google.com/icons)
- Framework: [Next.js](https://nextjs.org/) (React)
- Multilanguage support
- State Management: [Redux Toolkit](https://redux-toolkit.js.org)
- State Management: [React Context](https://reactjs.org/docs/context.html)
- Persistent app state using LocalStorage
- Support for many screen sizes and resolutions

223
package-lock.json generated
View File

@ -9,12 +9,10 @@
"@fontsource/material-icons-rounded": "^4.5.2",
"@fontsource/vollkorn": "^4.5.4",
"@fontsource/zen-maru-gothic": "^4.5.5",
"@reduxjs/toolkit": "^1.7.2",
"markdown-to-jsx": "^7.1.6",
"next": "^12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-redux": "^7.2.6",
"react-swipeable": "^6.2.0",
"react-tooltip": "^4.2.21",
"turndown": "^7.1.1"
@ -139,6 +137,7 @@
"version": "7.17.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
"integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
@ -442,29 +441,6 @@
"node": ">= 8"
}
},
"node_modules/@reduxjs/toolkit": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.7.2.tgz",
"integrity": "sha512-wwr3//Ar8ZhM9bS58O+HCIaMlR4Y6SNHfuszz9hKnQuFIKvwaL3Kmjo6fpDKUOjo4Lv54Yi299ed8rofCJ/Vjw==",
"dependencies": {
"immer": "^9.0.7",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1",
"reselect": "^4.1.5"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || 18.0.0-beta",
"react-redux": "^7.2.1 || ^8.0.0-beta"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@rushstack/eslint-patch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz",
@ -485,15 +461,6 @@
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || insiders"
}
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -515,33 +482,25 @@
"node_modules/@types/prop-types": {
"version": "15.7.4",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==",
"dev": true
},
"node_modules/@types/react": {
"version": "17.0.39",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
"integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-redux": {
"version": "7.1.22",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
"integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
"dev": true
},
"node_modules/@typescript-eslint/parser": {
"version": "5.12.0",
@ -1123,7 +1082,8 @@
"node_modules/csstype": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==",
"dev": true
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@ -2034,14 +1994,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/ignore": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
@ -2051,15 +2003,6 @@
"node": ">= 4"
}
},
"node_modules/immer": {
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz",
"integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -3116,35 +3059,6 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-redux": {
"version": "7.2.6",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
"integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==",
"dependencies": {
"@babel/runtime": "^7.15.4",
"@types/react-redux": "^7.1.20",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^17.0.2"
},
"peerDependencies": {
"react": "^16.8.3 || ^17"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/react-redux/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/react-swipeable": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-6.2.0.tgz",
@ -3181,26 +3095,11 @@
"node": ">=8.10.0"
}
},
"node_modules/redux": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz",
"integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==",
"dependencies": {
"@babel/runtime": "^7.9.2"
}
},
"node_modules/redux-thunk": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
"peerDependencies": {
"redux": "^4"
}
},
"node_modules/regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"dev": true
},
"node_modules/regexp.prototype.flags": {
"version": "1.4.1",
@ -3230,11 +3129,6 @@
"url": "https://github.com/sponsors/mysticatea"
}
},
"node_modules/reselect": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
"integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
},
"node_modules/resolve": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
@ -3857,6 +3751,7 @@
"version": "7.17.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
"integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
@ -4039,17 +3934,6 @@
"fastq": "^1.6.0"
}
},
"@reduxjs/toolkit": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.7.2.tgz",
"integrity": "sha512-wwr3//Ar8ZhM9bS58O+HCIaMlR4Y6SNHfuszz9hKnQuFIKvwaL3Kmjo6fpDKUOjo4Lv54Yi299ed8rofCJ/Vjw==",
"requires": {
"immer": "^9.0.7",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1",
"reselect": "^4.1.5"
}
},
"@rushstack/eslint-patch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz",
@ -4067,15 +3951,6 @@
"lodash.merge": "^4.6.2"
}
},
"@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"requires": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -4097,33 +3972,25 @@
"@types/prop-types": {
"version": "15.7.4",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==",
"dev": true
},
"@types/react": {
"version": "17.0.39",
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
"integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"@types/react-redux": {
"version": "7.1.22",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
"integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
"requires": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"@types/scheduler": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
"dev": true
},
"@typescript-eslint/parser": {
"version": "5.12.0",
@ -4518,7 +4385,8 @@
"csstype": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==",
"dev": true
},
"damerau-levenshtein": {
"version": "1.0.8",
@ -5217,25 +5085,12 @@
"has-symbols": "^1.0.2"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"ignore": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
"dev": true
},
"immer": {
"version": "9.0.12",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz",
"integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA=="
},
"import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -5964,26 +5819,6 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-redux": {
"version": "7.2.6",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
"integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==",
"requires": {
"@babel/runtime": "^7.15.4",
"@types/react-redux": "^7.1.20",
"hoist-non-react-statics": "^3.3.2",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-is": "^17.0.2"
},
"dependencies": {
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}
}
},
"react-swipeable": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-6.2.0.tgz",
@ -6008,24 +5843,11 @@
"picomatch": "^2.2.1"
}
},
"redux": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz",
"integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==",
"requires": {
"@babel/runtime": "^7.9.2"
}
},
"redux-thunk": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
"requires": {}
},
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"dev": true
},
"regexp.prototype.flags": {
"version": "1.4.1",
@ -6043,11 +5865,6 @@
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
"dev": true
},
"reselect": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz",
"integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ=="
},
"resolve": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",

View File

@ -12,12 +12,10 @@
"@fontsource/material-icons-rounded": "^4.5.2",
"@fontsource/vollkorn": "^4.5.4",
"@fontsource/zen-maru-gothic": "^4.5.5",
"@reduxjs/toolkit": "^1.7.2",
"markdown-to-jsx": "^7.1.6",
"next": "^12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-redux": "^7.2.6",
"react-swipeable": "^6.2.0",
"react-tooltip": "^4.2.21",
"turndown": "^7.1.1"

View File

@ -1,29 +0,0 @@
function applyTheme() {
if (!("theme" in localStorage)) {
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
localStorage.theme = "dark";
} else {
localStorage.theme = "light";
}
}
if (localStorage.theme === "dark") {
document.documentElement.classList.add("dark");
document.querySelector("#themeButtonIcon").innerHTML = "light_mode";
} else {
document.documentElement.classList.remove("dark");
document.querySelector("#themeButtonIcon").innerHTML = "dark_mode";
}
}
function toggleTheme() {
if (localStorage.theme === "dark") {
localStorage.theme = "light";
} else {
localStorage.theme = "dark";
}
applyTheme();
}
applyTheme();
document.querySelector("#themeButton").onclick = () => toggleTheme();

View File

@ -6,17 +6,8 @@ import { useRouter } from "next/router";
import Button from "components/Button";
import { prettyLanguage } from "queries/helpers";
import { useMediaCoarse, useMediaMobile } from "hooks/useMediaQuery";
import { useSelector, useDispatch } from "react-redux";
import {
setMainPanelOpen,
setLanguagePanelOpen,
setSubPanelOpen,
setMainPanelReduced,
} from "redux/appLayoutSlice";
import { RootState } from "redux/store";
import ReactTooltip from "react-tooltip";
import Script from "next/script";
import { useAppLayout } from "contexts/AppLayoutContext";
type AppLayoutProps = {
subPanel?: React.ReactNode;
@ -29,208 +20,203 @@ type AppLayoutProps = {
export default function AppLayout(props: AppLayoutProps): JSX.Element {
const titlePrefix = "Accords Library";
const router = useRouter();
const dispatch = useDispatch();
const isMobile = useMediaMobile();
const isCoarse = useMediaCoarse();
const languagePanelOpen = useSelector(
(state: RootState) => state.appLayout.languagePanelOpen
);
const mainPanelOpen = useSelector(
(state: RootState) => state.appLayout.mainPanelOpen
);
const mainPanelReduced = useSelector(
(state: RootState) => state.appLayout.mainPanelReduced
);
const subPanelOpen = useSelector(
(state: RootState) => state.appLayout.subPanelOpen
);
const appLayout = useAppLayout();
const sensibilitySwipe = 1.1;
const handlers = useSwipeable({
onSwipedLeft: (SwipeEventData) => {
if (SwipeEventData.velocity < sensibilitySwipe) return;
if (mainPanelOpen) {
dispatch(setMainPanelOpen(false));
if (appLayout.mainPanelOpen) {
appLayout.setMainPanelOpen(false);
} else if (props.subPanel && props.contentPanel) {
dispatch(setSubPanelOpen(true));
appLayout.setSubPanelOpen(true);
}
},
onSwipedRight: (SwipeEventData) => {
if (SwipeEventData.velocity < sensibilitySwipe) return;
if (subPanelOpen) {
dispatch(setSubPanelOpen(false));
if (appLayout.subPanelOpen) {
appLayout.setSubPanelOpen(false);
} else {
dispatch(setMainPanelOpen(true));
appLayout.setMainPanelOpen(true);
}
},
});
const mainPanelClass = `fixed desktop:left-0 desktop:top-0 desktop:bottom-0 ${
mainPanelReduced ? "desktop:w-[6rem]" : "desktop:w-[20rem]"
appLayout.mainPanelReduced ? "desktop:w-[6rem]" : "desktop:w-[20rem]"
}`;
const subPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:w-[20rem] ${
mainPanelReduced ? " desktop:left-[6rem]" : "desktop:left-[20rem]"
appLayout.mainPanelReduced ? " desktop:left-[6rem]" : "desktop:left-[20rem]"
}`;
let contentPanelClass = "";
if (props.subPanel) {
contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 ${
mainPanelReduced ? "desktop:left-[26rem]" : "desktop:left-[40rem]"
appLayout.mainPanelReduced
? "desktop:left-[26rem]"
: "desktop:left-[40rem]"
}`;
} else if (props.contentPanel) {
contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 ${
mainPanelReduced ? "desktop:left-[6rem]" : "desktop:left-[20rem]"
appLayout.mainPanelReduced
? "desktop:left-[6rem]"
: "desktop:left-[20rem]"
}`;
}
const turnSubIntoContent = props.subPanel && !props.contentPanel;
return (
<div {...handlers} className="touch-pan-y">
<Head>
<title>
{props.title ? `${titlePrefix} - ${props.title}` : titlePrefix}
</title>
</Head>
{/* Navbar */}
<div className="fixed bottom-0 left-0 right-0 h-20 border-t-[1px] border-black border-dotted grid grid-cols-[5rem_1fr_5rem] place-items-center desktop:hidden bg-light bg-paper bg-blend-multiply bg-local bg-[length:10cm]">
<span
id="navbar-main-button"
className="material-icons mt-[.1em] cursor-pointer"
onClick={() => dispatch(setMainPanelOpen(true))}
>
menu
</span>
<p className="text-2xl font-black font-headers">{props.title}</p>
<span
className="material-icons mt-[.1em] cursor-pointer"
onClick={() => dispatch(setSubPanelOpen(true))}
>
{props.subPanel && !turnSubIntoContent
? props.subPanelIcon
? props.subPanelIcon
: "tune"
: ""}
</span>
</div>
{/* Content panel */}
<div className={appLayout.darkMode ? "dark" : ""}>
<div
className={`top-0 left-0 right-0 bottom-20 overflow-y-scroll bg-light bg-paper bg-blend-multiply bg-local bg-[length:10cm] ${contentPanelClass}`}
{...handlers}
className="touch-pan-y p-0 m-0 bg-light dark:bg-dark-light text-black dark:text-dark-black mobile:text-[90%]"
>
{props.contentPanel ? (
props.contentPanel
) : (
<div className="grid place-content-center h-full">
<div className="text-dark border-dark border-2 border-dotted rounded-2xl p-8 grid grid-flow-col place-items-center gap-9 opacity-40">
<p className="text-4xl"></p>
<p className="text-2xl w-64">
Select one of the options in the sidebar
</p>
</div>
</div>
)}
</div>
<Head>
<title>
{props.title ? `${titlePrefix} - ${props.title}` : titlePrefix}
</title>
</Head>
{/* Background when navbar is opened */}
<div
className={`fixed bg-shade inset-0 transition-opacity duration-500
{/* Navbar */}
<div className="fixed bottom-0 left-0 right-0 h-20 border-t-[1px] border-black dark:border-dark-black border-dotted grid grid-cols-[5rem_1fr_5rem] place-items-center desktop:hidden bg-light dark:bg-dark-light bg-paper bg-blend-multiply bg-local bg-[length:10cm]">
<span
className="material-icons mt-[.1em] cursor-pointer"
onClick={() => appLayout.setMainPanelOpen(true)}
>
menu
</span>
<p className="text-2xl font-black font-headers">{props.title}</p>
<span
className="material-icons mt-[.1em] cursor-pointer"
onClick={() => appLayout.setSubPanelOpen(true)}
>
{props.subPanel && !turnSubIntoContent
? props.subPanelIcon
? props.subPanelIcon
: "tune"
: ""}
</span>
</div>
{/* Content panel */}
<div
className={`top-0 left-0 right-0 bottom-20 overflow-y-scroll bg-light dark:bg-dark-light bg-paper bg-blend-multiply bg-local bg-[length:10cm] ${contentPanelClass}`}
>
{props.contentPanel ? (
props.contentPanel
) : (
<div className="grid place-content-center h-full">
<div className="text-dark dark:text-dark-dark border-dark border-2 border-dotted rounded-2xl p-8 grid grid-flow-col place-items-center gap-9 opacity-40">
<p className="text-4xl"></p>
<p className="text-2xl w-64">
Select one of the options in the sidebar
</p>
</div>
</div>
)}
</div>
{/* Background when navbar is opened */}
<div
className={`fixed bg-shade dark:bg-dark-shade inset-0 transition-opacity duration-500
${turnSubIntoContent ? "z-10" : ""}
${
(mainPanelOpen || subPanelOpen) && isMobile
? "opacity-50"
: "opacity-0 pointer-events-none touch-none"
(appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile
? "opacity-60 dark:opacity-60"
: "opacity-0 dark:opacity-0 pointer-events-none touch-none"
}`}
onClick={() => {
dispatch(setMainPanelOpen(false));
dispatch(setSubPanelOpen(false));
}}
></div>
onClick={() => {
appLayout.setMainPanelOpen(false);
appLayout.setSubPanelOpen(false);
}}
></div>
{/* Sub panel */}
{props.subPanel ? (
<div
className={`${subPanelClass} border-r-[1px] mobile:border-r-0 mobile:border-l-[1px] border-black border-dotted top-0 bottom-0 right-0 left-12 overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-300 bg-light bg-paper bg-blend-multiply bg-local bg-[length:10cm]
{/* Sub panel */}
{props.subPanel ? (
<div
className={`${subPanelClass} border-r-[1px] mobile:border-r-0 mobile:border-l-[1px] border-black dark:border-dark-black border-dotted top-0 bottom-0 right-0 left-12 overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-300 bg-light dark:bg-dark-light bg-paper bg-blend-multiply bg-local bg-[length:10cm]
${
turnSubIntoContent
? "mobile:translate-x-0 mobile:bottom-20 mobile:left-0 mobile:border-l-0"
: !subPanelOpen
: !appLayout.subPanelOpen
? "mobile:translate-x-full"
: ""
}`}
>
{props.subPanel}
</div>
) : (
""
)}
>
{props.subPanel}
</div>
) : (
""
)}
{/* Main panel */}
<div
className={`${mainPanelClass} border-r-[1px] border-black border-dotted top-0 bottom-0 left-0 right-12 overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-300 z-20 bg-light bg-paper bg-blend-multiply bg-local bg-[length:10cm]
${mainPanelOpen ? "" : "mobile:-translate-x-full"}`}
>
<MainPanel langui={props.langui} />
</div>
{/* Main panel minimize button*/}
<div
className={`mobile:hidden translate-x-0 fixed top-1/2 z-20 ${
mainPanelReduced ? "left-[4.65rem]" : "left-[18.65rem]"
}`}
onClick={() => dispatch(setMainPanelReduced(!mainPanelReduced))}
>
<Button className="material-icons bg-light !px-2">
{mainPanelReduced ? "chevron_right" : "chevron_left"}
</Button>
</div>
{/* Language selection background */}
<div
className={`fixed bg-shade inset-0 transition-all duration-500 z-20 grid place-content-center ${
languagePanelOpen
? "bg-opacity-50"
: "bg-opacity-0 pointer-events-none touch-none"
}`}
onClick={() => {
dispatch(setLanguagePanelOpen(false));
}}
>
{/* Main panel */}
<div
className={`p-10 bg-light rounded-lg shadow-2xl shadow-shade grid gap-4 place-items-center transition-transform ${
languagePanelOpen ? "scale-100" : "scale-0"
}`}
className={`${mainPanelClass} border-r-[1px] border-black dark:border-dark-black border-dotted top-0 bottom-0 left-0 right-12 overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-300 z-20 bg-light dark:bg-dark-light bg-paper bg-blend-multiply bg-local bg-[length:10cm]
${appLayout.mainPanelOpen ? "" : "mobile:-translate-x-full"}`}
>
<h2 className="text-2xl">Select a language</h2>
<div className="flex flex-wrap flex-row gap-2">
{router.locales?.sort().map((locale) => (
<Button
key={locale}
active={locale === router.locale}
href={router.asPath}
locale={locale}
>
{prettyLanguage(locale)}
</Button>
))}
<MainPanel langui={props.langui} />
</div>
{/* Main panel minimize button*/}
<div
className={`mobile:hidden translate-x-0 fixed top-1/2 z-20 ${
appLayout.mainPanelReduced ? "left-[4.65rem]" : "left-[18.65rem]"
}`}
onClick={() =>
appLayout.setMainPanelReduced(!appLayout.mainPanelReduced)
}
>
<Button className="material-icons bg-light dark:bg-dark-light !px-2">
{appLayout.mainPanelReduced ? "chevron_right" : "chevron_left"}
</Button>
</div>
{/* Language selection background */}
<div
className={`fixed bg-shade dark:bg-dark-shade inset-0 transition-all duration-500 z-20 grid place-content-center ${
appLayout.languagePanelOpen
? "bg-opacity-60 dark:bg-opacity-60"
: "bg-opacity-0 dark:bg-opacity-0 pointer-events-none touch-none"
}`}
onClick={() => {
appLayout.setLanguagePanelOpen(false);
}}
>
<div
className={`p-10 bg-light dark:bg-dark-light rounded-lg shadow-2xl shadow-shade dark:shadow-dark-shade grid gap-4 place-items-center transition-transform ${
appLayout.languagePanelOpen ? "scale-100" : "scale-0"
}`}
>
<h2 className="text-2xl">Select a language</h2>
<div className="flex flex-wrap flex-row gap-2">
{router.locales?.sort().map((locale) => (
<Button
key={locale}
active={locale === router.locale}
href={router.asPath}
locale={locale}
>
{prettyLanguage(locale)}
</Button>
))}
</div>
</div>
</div>
<ReactTooltip
id="MainPanelTooltip"
place="right"
type="light"
effect="solid"
delayShow={300}
delayHide={100}
disable={!appLayout.mainPanelReduced || isMobile || isCoarse}
className="drop-shadow-shade-xl dark:drop-shadow-dark-shade-xl !opacity-100 !bg-light dark:!bg-dark-light !rounded-lg after:!border-r-light text-left !text-black dark:!text-dark-black"
/>
</div>
<ReactTooltip
id="MainPanelTooltip"
place="right"
type="light"
effect="solid"
delayShow={300}
delayHide={100}
disable={!mainPanelReduced || isMobile || isCoarse}
className="drop-shadow-shade-xl !opacity-100 !bg-light !rounded-lg after:!border-r-light text-left !text-black"
/>
<Script src="/js/toggleTheme.js" defer />
</div>
);
}

View File

@ -16,12 +16,12 @@ export default function Button(props: ButtonProps): JSX.Element {
<div
id={props.id}
onClick={props.onClick}
className={`grid place-content-center place-items-center border-[1px] border-dark text-dark rounded-full px-4 pt-[0.4rem] pb-[0.5rem] transition-all ${
className={`grid place-content-center place-items-center border-[1px] border-dark text-dark dark:text-dark-dark rounded-full px-4 pt-[0.4rem] pb-[0.5rem] transition-all ${
props.className
} ${
props.active
? "text-light bg-black drop-shadow-black-lg !border-black cursor-not-allowed"
: "cursor-pointer hover:text-light hover:bg-dark hover:drop-shadow-shade-lg active:bg-black active:drop-shadow-black-lg active:border-black"
? "text-light dark:text-dark-light bg-black dark:bg-dark-black drop-shadow-black-lg dark:drop-shadow-dark-black-lg !border-black dark:!border-dark-black cursor-not-allowed"
: "cursor-pointer hover:text-light dark:hover:text-dark-light hover:bg-dark dark:hover:bg-dark-dark hover:drop-shadow-shade-lg dark:hover:drop-shadow-dark-shade-lg active:bg-black dark:active:bg-dark-black active:drop-shadow-black-lg dark:active:drop-shadow-dark-black-lg active:border-black dark:active:border-dark-black"
}`}
>
{props.children}

View File

@ -53,7 +53,7 @@ export default function ChronologyItemComponent(
return (
<div
className="grid place-content-start grid-rows-[auto_1fr] grid-cols-[4em] py-4 px-8 rounded-2xl target:bg-mid target:py-8 target:my-4"
className="grid place-content-start grid-rows-[auto_1fr] grid-cols-[4em] py-4 px-8 rounded-2xl target:bg-mid dark:bg-dark-mid target:py-8 target:my-4"
id={generateAnchor(
props.item.attributes.year,
props.item.attributes.month,
@ -71,7 +71,7 @@ export default function ChronologyItemComponent(
""
)}
<p className="col-start-1 text-dark text-sm">
<p className="col-start-1 text-dark dark:text-dark-dark text-sm">
{generateDate(props.item.attributes.month, props.item.attributes.day)}
</p>
@ -86,7 +86,7 @@ export default function ChronologyItemComponent(
<p
className={
event.translations.length > 1
? "before:content-['-'] before:text-dark before:inline-block before:w-4 before:ml-[-1em] mt-2 whitespace-pre-line"
? "before:content-['-'] before:text-dark dark:before:text-dark-dark before:inline-block before:w-4 before:ml-[-1em] mt-2 whitespace-pre-line"
: "whitespace-pre-line"
}
>
@ -103,7 +103,7 @@ export default function ChronologyItemComponent(
</>
))}
<p className="text-dark text-xs inline-grid place-items-center grid-flow-col gap-1">
<p className="text-dark dark:text-dark-dark text-xs inline-grid place-items-center grid-flow-col gap-1">
{event.source.data ? (
"(" + event.source.data.attributes.name + ")"
) : (

View File

@ -11,7 +11,7 @@ export default function ChronologyYearComponent(
): JSX.Element {
return (
<div
className="target:bg-mid rounded-2xl target:py-4 target:my-4"
className="target:bg-mid dark:bg-dark-mid rounded-2xl target:py-4 target:my-4"
id={props.items.length > 1 ? props.year.toString() : undefined}
>
{props.items.map((item, index) => (

View File

@ -26,7 +26,7 @@ export default function ThumbnailHeader(
return (
<>
<div className="grid place-items-center gap-12 mb-12">
<div className="drop-shadow-shade-lg">
<div className="drop-shadow-shade-lg dark:drop-shadow-dark-shade-lg">
{content.thumbnail.data ? (
<Img
className=" rounded-xl"
@ -35,7 +35,7 @@ export default function ThumbnailHeader(
priority
/>
) : (
<div className="w-full aspect-[4/3] bg-light rounded-xl"></div>
<div className="w-full aspect-[4/3] bg-light dark:bg-dark-light rounded-xl"></div>
)}
</div>
<div className="grid place-items-center text-center">

View File

@ -7,7 +7,7 @@ export default function HorizontalLine(
): JSX.Element {
return (
<div
className={`h-0 w-full my-8 border-t-[3px] border-dotted border-black ${props.className}`}
className={`h-0 w-full my-8 border-t-[3px] border-dotted border-black dark:border-dark-black ${props.className}`}
></div>
);
}

View File

@ -8,7 +8,7 @@ export default function InsetBox(props: InsetBoxProps): JSX.Element {
return (
<div
id={props.id}
className={`w-full shadow-inner-sm shadow-shade bg-mid rounded-xl p-8 ${props.className}`}
className={`w-full shadow-inner-sm shadow-shade bg-mid dark:bg-dark-mid rounded-xl p-8 ${props.className}`}
>
{props.children}
</div>

View File

@ -21,7 +21,7 @@ export default function LibraryContentPreview(
return (
<Link href={"/contents/" + item.slug} passHref>
<div className="drop-shadow-shade-xl cursor-pointer grid items-end fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform">
<div className="drop-shadow-shade-xl dark:drop-shadow-dark-shade-xl cursor-pointer grid items-end fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform">
{item.thumbnail.data ? (
<Img
className="rounded-md coarse:rounded-b-none"
@ -29,9 +29,9 @@ export default function LibraryContentPreview(
quality={ImageQuality.Medium}
/>
) : (
<div className="w-full aspect-[3/2] bg-light rounded-lg"></div>
<div className="w-full aspect-[3/2] bg-light dark:bg-dark-light rounded-lg"></div>
)}
<div className="linearbg-obi fine:drop-shadow-shade-lg fine:absolute coarse:rounded-b-md bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-2">
<div className="linearbg-obi dark:linearbg-dark-obi fine:drop-shadow-shade-lg dark:fine:drop-shadow-dark-shade-lg fine:absolute coarse:rounded-b-md bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-2">
<div className="grid grid-flow-col gap-1 overflow-hidden place-content-start">
{item.type ? (
<Chip>

View File

@ -25,7 +25,7 @@ export default function LibraryItemsPreview(
return (
<Link href={"/library/" + item.slug} passHref>
<div
className={`drop-shadow-shade-xl cursor-pointer grid items-end hover:rounded-3xl fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform ${props.className}`}
className={`drop-shadow-shade-xl dark:drop-shadow-dark-shade-xl cursor-pointer grid items-end hover:rounded-3xl fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform ${props.className}`}
>
{item.thumbnail.data ? (
<Img
@ -33,10 +33,10 @@ export default function LibraryItemsPreview(
quality={ImageQuality.Medium}
/>
) : (
<div className="w-full aspect-[21/29.7] bg-light rounded-lg"></div>
<div className="w-full aspect-[21/29.7] bg-light dark:bg-dark-light rounded-lg"></div>
)}
<div className="linearbg-obi fine:drop-shadow-shade-lg fine:absolute place-items-start bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-2">
<div className="linearbg-obi dark:linearbg-dark-obi fine:drop-shadow-shade-lg dark:fine:drop-shadow-dark-shade-lg fine:absolute place-items-start bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-2">
{item.metadata && item.metadata.length > 0 ? (
<div className="flex flex-row gap-1">
<Chip>{prettyItemSubType(item.metadata[0])}</Chip>

View File

@ -9,7 +9,7 @@ type ScenBreakProps = {
export default function Markdawn(props: ScenBreakProps): JSX.Element {
return (
<Markdown
className={`prose prose-p:text-justify text-black ${props.className}`}
className={`prose prose-p:text-justify text-black dark:text-dark-black ${props.className}`}
options={{
overrides: {
hr: {

View File

@ -6,7 +6,7 @@ export default function SceneBreak(props: ScenBreakProps): JSX.Element {
return (
<div
className={
"h-0 text-center text-3xl text-dark mt-16 mb-20" + " " + props.className
"h-0 text-center text-3xl text-dark dark:text-dark-dark mt-16 mb-20" + " " + props.className
}
>
* * *

View File

@ -16,10 +16,10 @@ type NavOptionProps = {
export default function NavOption(props: NavOptionProps): JSX.Element {
const router = useRouter();
const isActive = router.asPath.startsWith(props.url);
const divActive = "bg-mid shadow-inner-sm shadow-shade";
const divActive = "bg-mid dark:bg-dark-mid shadow-inner-sm shadow-shade dark:shadow-dark-shade";
const border =
"outline outline-mid outline-2 outline-offset-[-2px] hover:outline-[transparent]";
const divCommon = `gap-x-5 w-full rounded-2xl cursor-pointer p-4 hover:bg-mid hover:shadow-inner-sm hover:shadow-shade hover:active:shadow-inner hover:active:shadow-shade transition-all ${
"outline outline-mid dark:outline-dark-mid outline-2 outline-offset-[-2px] hover:outline-[transparent]";
const divCommon = `gap-x-5 w-full rounded-2xl cursor-pointer p-4 hover:bg-mid dark:hover:bg-dark-mid hover:shadow-inner-sm hover:shadow-shade dark:hover:shadow-dark-shade hover:active:shadow-inner hover:active:shadow-shade dark:hover:active:shadow-dark-shade transition-all ${
props.border ? border : ""
} ${isActive ? divActive : ""}`;

View File

@ -1,7 +1,6 @@
import Button from "components/Button";
import { useAppLayout } from "contexts/AppLayoutContext";
import { GetWebsiteInterfaceQuery } from "graphql/operations-types";
import { useDispatch } from "react-redux";
import { setSubPanelOpen } from "redux/appLayoutSlice";
type ReturnButtonProps = {
href: string;
@ -10,10 +9,10 @@ type ReturnButtonProps = {
};
export default function ReturnButton(props: ReturnButtonProps): JSX.Element {
const dispatch = useDispatch();
const appLayout = useAppLayout();
return (
<Button onClick={() => dispatch(setSubPanelOpen(false))} href={props.href}>
<Button onClick={() => appLayout.setSubPanelOpen(false)} href={props.href}>
&emsp;{props.langui.global_return_label} {props.title}
</Button>
);

View File

@ -6,10 +6,8 @@ import Button from "components/Button";
import HorizontalLine from "components/HorizontalLine";
import { GetWebsiteInterfaceQuery } from "graphql/operations-types";
import Markdown from "markdown-to-jsx";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "redux/store";
import { setLanguagePanelOpen, setMainPanelOpen } from "redux/appLayoutSlice";
import { useMediaDesktop } from "hooks/useMediaQuery";
import { useAppLayout } from "contexts/AppLayoutContext";
type MainPanelProps = {
langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"];
@ -19,32 +17,34 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
const langui = props.langui;
const router = useRouter();
const isDesktop = useMediaDesktop();
const mainPanelReduced = useSelector(
(state: RootState) => state.appLayout.mainPanelReduced
);
const dispatch = useDispatch();
const appLayout = useAppLayout();
return (
<div
id="mainPanel"
className={`flex flex-col justify-center content-start gap-y-2 justify-items-center text-center p-8 ${
mainPanelReduced && "px-4"
appLayout.mainPanelReduced && "px-4"
}`}
>
{mainPanelReduced && isDesktop ? (
{appLayout.mainPanelReduced && isDesktop ? (
<div className="grid place-items-center gap-4">
<Link href="/" passHref>
<div className="w-12 cursor-pointer transition-[filter] colorize-black hover:colorize-dark">
<div
onClick={() => appLayout.setMainPanelOpen(false)}
className="w-12 cursor-pointer transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark"
>
<SVG
src={"/icons/accords.svg"}
alt={"Logo of Accord's Library"}
/>
</div>
</Link>
<Button onClick={() => appLayout.setDarkMode(!appLayout.darkMode)}>
<span className="material-icons !text-sm">
{appLayout.darkMode ? "light_mode" : "dark_mode"}
</span>
</Button>
{router.locale ? (
<div onClick={() => dispatch(setLanguagePanelOpen(true))}>
<div onClick={() => appLayout.setLanguagePanelOpen(true)}>
<Button className="text-xs">{router.locale.toUpperCase()}</Button>
</div>
) : (
@ -55,7 +55,10 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
<div>
<div className="grid place-items-center">
<Link href="/" passHref>
<div className="w-1/2 cursor-pointer transition-[filter] colorize-black hover:colorize-dark">
<div
onClick={() => appLayout.setMainPanelOpen(false)}
className="w-1/2 cursor-pointer transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark"
>
<SVG
src={"/icons/accords.svg"}
alt={"Logo of Accord's Library"}
@ -67,17 +70,17 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
<div className="flex flex-row flex-wrap gap-2">
<Button
id="themeButton"
onClick={() => appLayout.setDarkMode(!appLayout.darkMode)}
className="right-0 top-[-1.3em] !py-0.5 !px-2.5"
>
<span id="themeButtonIcon" className="material-icons !text-sm">
dark_mode
<span className="material-icons !text-sm">
{appLayout.darkMode ? "dark_mode" : "light_mode"}
</span>
</Button>
{router.locale && (
<Button
onClick={() => dispatch(setLanguagePanelOpen(true))}
onClick={() => appLayout.setLanguagePanelOpen(true)}
className="right-0 top-[-1.3em] text-sm !py-0.5 !px-2.5"
>
{router.locale.toUpperCase()}
@ -88,16 +91,14 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
</div>
)}
<HorizontalLine />
<NavOption
url="/library"
icon="library_books"
title={langui.main_library}
subtitle={langui.main_library_description}
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
<NavOption
@ -106,8 +107,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
title="Contents"
subtitle="Explore all content and filter by type or category"
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
<NavOption
@ -116,8 +117,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
title={langui.main_wiki}
subtitle={langui.main_wiki_description}
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
<NavOption
@ -126,8 +127,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
title={langui.main_chronicles}
subtitle={langui.main_chronicles_description}
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
<HorizontalLine />
@ -137,8 +138,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
icon="feed"
title={langui.main_news}
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
<NavOption
@ -146,8 +147,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
icon="store"
title={langui.main_merch}
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
<NavOption
@ -155,8 +156,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
icon="collections"
title={langui.main_gallery}
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
<NavOption
@ -164,8 +165,8 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
icon="inventory"
title={langui.main_archives}
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
<NavOption
@ -173,15 +174,15 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
icon="info"
title={langui.main_about_us}
tooltipId="MainPanelTooltip"
reduced={mainPanelReduced && isDesktop}
onClick={() => dispatch(setMainPanelOpen(false))}
reduced={appLayout.mainPanelReduced && isDesktop}
onClick={() => appLayout.setMainPanelOpen(false)}
/>
{mainPanelReduced && isDesktop ? "" : <HorizontalLine />}
{appLayout.mainPanelReduced && isDesktop ? "" : <HorizontalLine />}
<div
className={`text-center ${
mainPanelReduced && isDesktop ? "hidden" : ""
appLayout.mainPanelReduced && isDesktop ? "hidden" : ""
}`}
>
<p>
@ -192,7 +193,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
)}
</p>
<a
className="transition-[filter] colorize-black hover:colorize-dark"
className="transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark"
href="https://creativecommons.org/licenses/by-sa/4.0/"
>
<div className="mt-4 mb-8 grid grid-flow-col place-content-center gap-1">
@ -222,7 +223,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
</p>
<div className="mt-12 mb-4 grid h-4 grid-flow-col place-content-center gap-8">
<a
className="transition-[filter] colorize-black hover:colorize-dark"
className="transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark"
href="https://github.com/Accords-Library"
target="_blank"
rel="noopener noreferrer"
@ -230,7 +231,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
<SVG className="w-10" src={"/icons/github-brands.svg"} alt={""} />
</a>
<a
className="transition-[filter] colorize-black hover:colorize-dark"
className="transition-[filter] colorize-black dark:colorize-dark-black hover:colorize-dark dark:hover:colorize-dark-dark"
href="/discord"
target="_blank"
rel="noopener noreferrer"

View File

@ -0,0 +1,99 @@
import React, { ReactNode, useContext, useEffect, useState } from "react";
export interface AppLayoutState {
subPanelOpen: boolean;
languagePanelOpen: boolean;
mainPanelReduced: boolean;
mainPanelOpen: boolean;
darkMode: boolean;
setSubPanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
setLanguagePanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
setMainPanelReduced: React.Dispatch<React.SetStateAction<boolean>>;
setMainPanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
setDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
}
const initialState: AppLayoutState = {
subPanelOpen: false,
languagePanelOpen: false,
mainPanelReduced: false,
mainPanelOpen: false,
darkMode: false,
setSubPanelOpen: () => {},
setLanguagePanelOpen: () => {},
setMainPanelReduced: () => {},
setMainPanelOpen: () => {},
setDarkMode: () => {},
};
const AppContext = React.createContext<AppLayoutState>(initialState);
export default AppContext;
export function useAppLayout() {
return useContext(AppContext);
}
type Props = {
children: ReactNode;
};
export const AppContextProvider = (props: Props) => {
function useLocalStorage(
value: boolean,
setter: React.Dispatch<React.SetStateAction<boolean>>,
name: string
): void {
useEffect(() => {
const data = localStorage.getItem(name);
if (data) setter(JSON.parse(data));
}, [setter, name]);
useEffect(() => {
localStorage.setItem(name, JSON.stringify(value));
}, [value, name]);
}
const [subPanelOpen, setSubPanelOpen] = useState<boolean>(
initialState.subPanelOpen
);
const [languagePanelOpen, setLanguagePanelOpen] = useState<boolean>(
initialState.languagePanelOpen
);
const [mainPanelReduced, setMainPanelReduced] = useState<boolean>(
initialState.mainPanelReduced
);
const [mainPanelOpen, setMainPanelOpen] = useState<boolean>(
initialState.mainPanelOpen
);
const [darkMode, setDarkMode] = useState<boolean>(initialState.darkMode);
useLocalStorage(subPanelOpen, setSubPanelOpen, "subPanelOpen");
useLocalStorage(languagePanelOpen, setLanguagePanelOpen, "languagePanelOpen");
useLocalStorage(mainPanelReduced, setMainPanelReduced, "mainPanelReduced");
useLocalStorage(mainPanelOpen, setMainPanelOpen, "mainPanelOpen");
useLocalStorage(darkMode, setDarkMode, "darkMode");
return (
<AppContext.Provider
value={{
subPanelOpen,
languagePanelOpen,
mainPanelReduced,
mainPanelOpen,
darkMode,
setSubPanelOpen,
setLanguagePanelOpen,
setMainPanelReduced,
setMainPanelOpen,
setDarkMode,
}}
>
{props.children}
</AppContext.Provider>
);
};

View File

@ -4,13 +4,12 @@ import "@fontsource/zen-maru-gothic/500.css";
import "@fontsource/vollkorn/700.css";
import "@fontsource/material-icons";
import { store } from "redux/store";
import { Provider } from "react-redux";
import { AppContextProvider } from "contexts/AppLayoutContext";
export default function AccordsLibraryApp(appProps: AppProps) {
return (
<Provider store={store}>
<AppContextProvider>
<appProps.Component {...appProps.pageProps} />
</Provider>
</AppContextProvider>
);
}

View File

@ -43,7 +43,7 @@ export default function Editor(props: EditorProps): JSX.Element {
<textarea
id="editorTextArea"
onInput={handleInput}
className="bg-mid rounded-xl p-8 w-full font-monospace"
className="bg-mid dark:bg-dark-mid rounded-xl p-8 w-full font-monospace"
value={markdown}
/>
@ -71,12 +71,12 @@ export default function Editor(props: EditorProps): JSX.Element {
target.select();
event.preventDefault();
}}
className="bg-mid rounded-xl p-8 w-full font-monospace"
className="bg-mid dark:bg-dark-mid rounded-xl p-8 w-full font-monospace"
/>
</div>
<div>
<h2>Preview</h2>
<div className="bg-mid rounded-xl p-8">
<div className="bg-mid dark:bg-dark-mid rounded-xl p-8">
<Markdawn className="max-w-full" text={markdown} />
</div>
</div>

View File

@ -31,9 +31,8 @@ import HorizontalLine from "components/HorizontalLine";
import AppLayout from "components/AppLayout";
import LibraryItemsPreview from "components/Library/LibraryItemsPreview";
import InsetBox from "components/InsetBox";
import { setSubPanelOpen } from "redux/appLayoutSlice";
import { useDispatch } from "react-redux";
import Img, { ImageQuality } from "components/Img";
import { useAppLayout } from "contexts/AppLayoutContext";
type LibrarySlugProps = {
libraryItem: GetLibraryItemQuery;
@ -43,7 +42,7 @@ type LibrarySlugProps = {
export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
const item = props.libraryItem.libraryItems.data[0].attributes;
const langui = props.langui.websiteInterfaces.data[0].attributes;
const dispatch = useDispatch();
const appLayout = useAppLayout();
const isVariantSet =
item.metadata.length > 0 &&
@ -64,7 +63,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
title={langui.library_item_summary}
url="#summary"
border
onClick={() => dispatch(setSubPanelOpen(false))}
onClick={() => appLayout.setSubPanelOpen(false)}
/>
{item.gallery.data.length > 0 ? (
@ -72,7 +71,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
title={langui.library_item_gallery}
url="#gallery"
border
onClick={() => dispatch(setSubPanelOpen(false))}
onClick={() => appLayout.setSubPanelOpen(false)}
/>
) : (
""
@ -82,7 +81,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
title={langui.library_item_details}
url="#details"
border
onClick={() => dispatch(setSubPanelOpen(false))}
onClick={() => appLayout.setSubPanelOpen(false)}
/>
{item.subitems.data.length > 0 ? (
@ -94,7 +93,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
}
url={isVariantSet ? "#variants" : "#subitems"}
border
onClick={() => dispatch(setSubPanelOpen(false))}
onClick={() => appLayout.setSubPanelOpen(false)}
/>
) : (
""
@ -116,7 +115,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}>
<div className="grid place-items-center gap-12">
<div className="drop-shadow-shade-xl w-full h-[50vh] mobile:h-[80vh] mb-16 relative cursor-pointer">
<div className="drop-shadow-shade-xl dark:drop-shadow-dark-shade-xl w-full h-[50vh] mobile:h-[80vh] mb-16 relative cursor-pointer">
{item.thumbnail.data ? (
<Img
image={item.thumbnail.data.attributes}
@ -126,7 +125,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
priority
/>
) : (
<div className="w-full aspect-[21/29.7] bg-light rounded-xl"></div>
<div className="w-full aspect-[21/29.7] bg-light dark:bg-dark-light rounded-xl"></div>
)}
</div>
@ -173,7 +172,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
key={galleryItem.id}
className="relative aspect-square hover:scale-[1.02] transition-transform cursor-pointer"
>
<div className="bg-light absolute inset-0 rounded-lg drop-shadow-shade-md"></div>
<div className="bg-light dark:bg-dark-light absolute inset-0 rounded-lg drop-shadow-shade-md dark:drop-shadow-dark-shade-md"></div>
<Img
className="rounded-lg"
image={galleryItem.attributes}
@ -375,7 +374,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
<div
id={content.attributes.slug}
key={content.id}
className="grid gap-2 px-4 rounded-lg target:bg-mid target:shadow-inner-sm target:shadow-shade target:h-auto target:py-3 target:my-2 target:[--displaySubContentMenu:grid] [--displaySubContentMenu:none]"
className="grid gap-2 px-4 rounded-lg target:bg-mid dark:bg-dark-mid target:shadow-inner-sm target:shadow-shade target:h-auto target:py-3 target:my-2 target:[--displaySubContentMenu:grid] [--displaySubContentMenu:none]"
>
<div className="grid gap-4 place-items-center grid-cols-[auto_auto_1fr_auto_12ch] thin:grid-cols-[auto_auto_1fr_auto]">
<a href={`#${content.attributes.slug}`}>
@ -403,7 +402,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
)
)}
</div>
<p className="border-b-2 h-4 w-full border-black border-dotted opacity-30"></p>
<p className="border-b-2 h-4 w-full border-black dark:border-dark-black border-dotted opacity-30"></p>
<p>
{content.attributes.range[0].__typename ===
"ComponentRangePageRange"
@ -426,7 +425,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
)}
</div>
<div className="grid-flow-col place-content-start place-items-center gap-2 [display:var(--displaySubContentMenu)]">
<span className="material-icons text-dark">
<span className="material-icons text-dark dark:text-dark-dark">
subdirectory_arrow_right
</span>

View File

@ -1,47 +0,0 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export interface AppLayoutState {
subPanelOpen: boolean;
languagePanelOpen: boolean;
mainPanelReduced: boolean;
mainPanelOpen: boolean;
}
const initialState: AppLayoutState = {
subPanelOpen: false,
languagePanelOpen: false,
mainPanelReduced: false,
mainPanelOpen: false,
};
export const appLayoutSlice = createSlice({
name: "appLayout",
initialState,
reducers: {
setMainPanelOpen: (state, action: PayloadAction<boolean>) => {
state.mainPanelOpen = action.payload;
},
setLanguagePanelOpen: (state, action: PayloadAction<boolean>) => {
state.languagePanelOpen = action.payload;
},
setSubPanelOpen: (state, action: PayloadAction<boolean>) => {
state.subPanelOpen = action.payload;
},
setMainPanelReduced: (state, action: PayloadAction<boolean>) => {
state.mainPanelReduced = action.payload;
},
},
});
// Action creators are generated for each case reducer function
export const {
setMainPanelOpen,
setLanguagePanelOpen,
setSubPanelOpen,
setMainPanelReduced,
} = appLayoutSlice.actions;
export default appLayoutSlice.reducer;

View File

@ -1,11 +0,0 @@
import { configureStore } from "@reduxjs/toolkit";
import appLayoutReducer from "redux/appLayoutSlice";
export const store = configureStore({
reducer: { appLayout: appLayoutReducer },
});
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;

View File

@ -3,10 +3,6 @@
@tailwind utilities;
@layer base {
html,
body {
@apply p-0 m-0 bg-light text-black mobile:text-[90%];
}
* {
@apply box-border font-body font-medium scroll-smooth scroll-m-8;
@ -22,18 +18,17 @@
}
a {
@apply transition-colors underline-offset-2 decoration-dotted underline decoration-dark hover:text-dark;
@apply transition-colors underline-offset-2 decoration-dotted underline decoration-dark dark:decoration-dark-dark hover:text-dark dark:hover:text-dark-dark;
}
*::selection {
@apply bg-dark text-light;
@apply bg-dark dark:bg-dark-dark text-light dark:text-dark-light;
}
/* SCROLLBARS STYLING */
* {
scrollbar-color: theme("colors.dark") transparent;
scrollbar-width: thin;
@apply [scrollbar-color:theme(colors.dark)_transparent] dark:[scrollbar-color:theme(colors.dark-dark)_transparent] [scrollbar-width:thin];
}
*::-webkit-scrollbar {
@ -41,22 +36,17 @@
}
*::-webkit-scrollbar-track {
@apply bg-[transparent];
@apply bg-light dark:bg-dark-light;
}
*::-webkit-scrollbar-thumb {
@apply bg-dark rounded-full border-[3px] border-solid border-light;
@apply bg-dark dark:bg-dark-dark rounded-full border-[3px] border-solid border-light dark:border-dark-light;
}
/* CHANGE PROSE DEFAULTS */
.prose a {
@apply transition-colors underline-offset-2 decoration-dotted underline decoration-dark hover:text-dark;
}
.prose {
--tw-prose-bullets: theme("colors.dark") !important;
--tw-prose-quote-borders: theme("colors.dark") !important;
@apply transition-colors underline-offset-2 decoration-dotted underline decoration-dark dark:decoration-dark-dark hover:text-dark dark:hover:text-dark-dark;
}
.prose footer {
@ -68,6 +58,6 @@
}
.prose footer > div:target {
@apply bg-mid shadow-inner-sm shadow-shade;
@apply bg-mid dark:bg-dark-mid shadow-inner-sm shadow-shade dark:shadow-dark-shade;
}
}