Added darkmode and replaced redux by context
This commit is contained in:
parent
a39313c655
commit
c4ce6aa11e
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
|
@ -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 = "Accord’s 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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 + ")"
|
||||
) : (
|
||||
|
|
|
@ -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) => (
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
>
|
||||
* * *
|
||||
|
|
|
@ -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 : ""}`;
|
||||
|
||||
|
|
|
@ -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}>
|
||||
❮ {props.langui.global_return_label} {props.title}
|
||||
</Button>
|
||||
);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue