123 files changed
+2521
-2287
No files matched your search
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "8.5.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-react-jsx",
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
"transform-class-properties"
|
||||
]
|
||||
}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
Generated
+370
-16
@@ -1529,27 +1529,124 @@
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-typescript": {
|
||||
"version": "7.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.5.2.tgz",
|
||||
"integrity": "sha512-r4zJOMbKY5puETm8+cIpaa0RQZG/sSASW1u0pj8qYklcERgVIbxVbP2wyJA7zI1//h7lEagQmXi9IL9iI5rfsA==",
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.6.3.tgz",
|
||||
"integrity": "sha512-aiWINBrPMSC3xTXRNM/dfmyYuPNKY/aexYqBgh0HBI5Y+WO5oRAqW/oROYeYHrF4Zw12r9rK4fMk/ZlAmqx/FQ==",
|
||||
"requires": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.5.0",
|
||||
"@babel/helper-create-class-features-plugin": "^7.6.0",
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"@babel/plugin-syntax-typescript": "^7.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/code-frame": {
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
|
||||
"integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz",
|
||||
"integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.6.3",
|
||||
"jsesc": "^2.5.1",
|
||||
"lodash": "^4.17.13",
|
||||
"source-map": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.0.tgz",
|
||||
"integrity": "sha512-EAoMc3hE5vE5LNhMqDOwB1usHvmRjCDAnH8CD4PVkX9/Yr3W/tcz8xE8QvdZxfsFBDICwZnF2UTHIqslRpvxmA==",
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.6.0.tgz",
|
||||
"integrity": "sha512-O1QWBko4fzGju6VoVvrZg0RROCVifcLxiApnGP3OWfWzvxRZFCoBD81K5ur5e3bVY2Vf/5rIJm8cqPKn8HUJng==",
|
||||
"requires": {
|
||||
"@babel/helper-function-name": "^7.1.0",
|
||||
"@babel/helper-member-expression-to-functions": "^7.0.0",
|
||||
"@babel/helper-member-expression-to-functions": "^7.5.5",
|
||||
"@babel/helper-optimise-call-expression": "^7.0.0",
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"@babel/helper-replace-supers": "^7.4.4",
|
||||
"@babel/helper-replace-supers": "^7.5.5",
|
||||
"@babel/helper-split-export-declaration": "^7.4.4"
|
||||
}
|
||||
},
|
||||
"@babel/helper-member-expression-to-functions": {
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz",
|
||||
"integrity": "sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.5.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-replace-supers": {
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz",
|
||||
"integrity": "sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==",
|
||||
"requires": {
|
||||
"@babel/helper-member-expression-to-functions": "^7.5.5",
|
||||
"@babel/helper-optimise-call-expression": "^7.0.0",
|
||||
"@babel/traverse": "^7.5.5",
|
||||
"@babel/types": "^7.5.5"
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz",
|
||||
"integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A=="
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz",
|
||||
"integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.5.5",
|
||||
"@babel/generator": "^7.6.3",
|
||||
"@babel/helper-function-name": "^7.1.0",
|
||||
"@babel/helper-split-export-declaration": "^7.4.4",
|
||||
"@babel/parser": "^7.6.3",
|
||||
"@babel/types": "^7.6.3",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0",
|
||||
"lodash": "^4.17.13"
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz",
|
||||
"integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==",
|
||||
"requires": {
|
||||
"esutils": "^2.0.2",
|
||||
"lodash": "^4.17.13",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
|
||||
},
|
||||
"jsesc": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1720,12 +1817,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/preset-typescript": {
|
||||
"version": "7.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz",
|
||||
"integrity": "sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg==",
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.6.0.tgz",
|
||||
"integrity": "sha512-4xKw3tTcCm0qApyT6PqM9qniseCE79xGHiUnNdKGdxNsGUc2X7WwZybqIpnTmoukg3nhPceI5KPNzNqLNeIJww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"@babel/plugin-transform-typescript": "^7.3.2"
|
||||
"@babel/plugin-transform-typescript": "^7.6.0"
|
||||
}
|
||||
},
|
||||
"@babel/register": {
|
||||
@@ -1862,12 +1960,187 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/accepts": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz",
|
||||
"integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/body-parser": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz",
|
||||
"integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/bson": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.0.tgz",
|
||||
"integrity": "sha512-pq/rqJwJWkbS10crsG5bgnrisL8pML79KlMKQMoQwLUjlPAkrUHMvHJ3oGwE7WHR61Lv/nadMwXVAD2b+fpD8Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/classnames": {
|
||||
"version": "2.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.9.tgz",
|
||||
"integrity": "sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz",
|
||||
"integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/cookies": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.4.tgz",
|
||||
"integrity": "sha512-oTGtMzZZAVuEjTwCjIh8T8FrC8n/uwy+PG0yTvQcdZ7etoel7C7/3MSd7qrukENTgQtotG7gvBlBojuVs7X5rw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/connect": "*",
|
||||
"@types/express": "*",
|
||||
"@types/keygrip": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz",
|
||||
"integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.16.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz",
|
||||
"integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/http-assert": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz",
|
||||
"integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/keygrip": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz",
|
||||
"integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/koa": {
|
||||
"version": "2.0.50",
|
||||
"resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.50.tgz",
|
||||
"integrity": "sha512-TcgOD2lh0EISSadAk1DOBYw7kNoY9XdeB3vEMOKiDDaTMYm+V54nyPsU7Ulb/htb5OBIR79RgTeCWntCcophLw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/accepts": "*",
|
||||
"@types/cookies": "*",
|
||||
"@types/http-assert": "*",
|
||||
"@types/keygrip": "*",
|
||||
"@types/koa-compose": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/koa-compose": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.4.tgz",
|
||||
"integrity": "sha512-ioou0rxkuWL+yBQYsHUQAzRTfVxAg8Y2VfMftU+Y3RA03/MzuFL0x/M2sXXj3PkfnENbHsjeHR1aMdezLYpTeA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/koa": "*"
|
||||
}
|
||||
},
|
||||
"@types/koa-route": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/koa-route/-/koa-route-3.2.4.tgz",
|
||||
"integrity": "sha512-jB4I4/HppiHiSFMqQvMC2Px5Zm8CExA7Bw7xW+o60xh4v0LrNTfKV7LMCGuJNRuUuTTiRcJvF1rbq5mINgQ1UQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/koa": "*",
|
||||
"path-to-regexp": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
|
||||
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mongodb": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.3.6.tgz",
|
||||
"integrity": "sha512-bPv0lGnkL/iOoA+GSAcS5ad9QyPc/rW547vrF5j+AKX4spaMZgpaCWQ4D6ipVSN6OKuks3ueCFEPinyJJSk2zA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/bson": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.12.tgz",
|
||||
"integrity": "sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
|
||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/q": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
|
||||
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.9.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.5.tgz",
|
||||
"integrity": "sha512-jQ12VMiFOWYlp+j66dghOWcmDDwhca0bnlcTxS4Qz/fh5gi6wpaZDthPEu/Gc/YlAuO87vbiUXL8qKstFvuOaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
|
||||
"integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/mime": "*"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
|
||||
@@ -2282,6 +2555,12 @@
|
||||
"readable-stream": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz",
|
||||
"integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "http://storage.mds.yandex.net/get-npm/69187/argparse-1.0.9.tgz",
|
||||
@@ -2559,9 +2838,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"is-buffer": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
|
||||
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
|
||||
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5235,6 +5514,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz",
|
||||
"integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==",
|
||||
"dev": true
|
||||
},
|
||||
"currently-unhandled": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "http://storage.mds.yandex.net/get-npm/38095/currently-unhandled-0.4.1.tgz",
|
||||
@@ -8415,6 +8700,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
|
||||
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
|
||||
"dev": true
|
||||
},
|
||||
"mamacro": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
|
||||
@@ -9346,6 +9637,15 @@
|
||||
"worker-farm": "1.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": {
|
||||
"version": "7.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz",
|
||||
"integrity": "sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg==",
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"@babel/plugin-transform-typescript": "^7.3.2"
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.0.tgz",
|
||||
@@ -15541,6 +15841,43 @@
|
||||
"integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=",
|
||||
"dev": true
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.4.1.tgz",
|
||||
"integrity": "sha512-5LpRN+mTiCs7lI5EtbXmF/HfMeCjzt7DH9CZwtkr6SywStrNQC723wG+aOWFiLNn7zT3kD/RnFqi3ZUfr4l5Qw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"source-map-support": "^0.5.6",
|
||||
"yn": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"diff": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
|
||||
"integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.13",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
|
||||
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
||||
@@ -15594,6 +15931,12 @@
|
||||
"resolved": "http://storage.mds.yandex.net/get-npm/35308/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
|
||||
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
|
||||
"dev": true
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.14",
|
||||
"resolved": "http://storage.mds.yandex.net/get-npm/66641/ua-parser-js-0.7.14.tgz",
|
||||
@@ -15785,6 +16128,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"use-media": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/use-media/-/use-media-1.4.0.tgz",
|
||||
"integrity": "sha512-XsgyUAf3nhzZmEfhc5MqLHwyaPjs78bgytpVJ/xDl0TF4Bptf3vEpBNBBT/EIKOmsOc8UbuECq3mrP3mt1QANA=="
|
||||
},
|
||||
"util": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
|
||||
@@ -16617,6 +16965,12 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz",
|
||||
"integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ=="
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-2
@@ -6,7 +6,8 @@
|
||||
"main": "src/bin/www.js",
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=production node src/server.js",
|
||||
"dev": "node src/server.js",
|
||||
"dev": "ts-node --project server.tsconfig.json src/server.ts",
|
||||
"next-dev": "next dev",
|
||||
"build": "next build && next export -o dist && npm run sitemap && npm run feed && npm run robots-txt",
|
||||
"robots-txt": "node scripts/robots-txt.js",
|
||||
"sitemap": "node scripts/sitemap.js",
|
||||
@@ -44,7 +45,8 @@
|
||||
"redux-thunk": "^2.2.0",
|
||||
"request-promise": "^2.0.1",
|
||||
"sitemap": "^1.5.0",
|
||||
"thunkify": "^2.1.2"
|
||||
"thunkify": "^2.1.2",
|
||||
"use-media": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.4.4",
|
||||
@@ -54,7 +56,14 @@
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.4.4",
|
||||
"@babel/polyfill": "^7.0.0-beta.47",
|
||||
"@babel/preset-env": "^7.4.5",
|
||||
"@babel/preset-typescript": "^7.6.0",
|
||||
"@babel/register": "^7.0.0-beta.47",
|
||||
"@types/classnames": "^2.2.9",
|
||||
"@types/koa": "^2.0.50",
|
||||
"@types/koa-route": "^3.2.4",
|
||||
"@types/mongodb": "^3.3.6",
|
||||
"@types/node": "^12.7.12",
|
||||
"@types/react": "^16.9.5",
|
||||
"@zeit/next-css": "^1.0.1",
|
||||
"@zeit/next-sass": "^1.0.1",
|
||||
"autoprefixer": "^6.3.6",
|
||||
@@ -73,6 +82,8 @@
|
||||
"mocha": "^6.1.4",
|
||||
"node-sass": "^4.12.0",
|
||||
"should": "^8.2.2",
|
||||
"ts-node": "^8.4.1",
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.32.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from 'react'
|
||||
import App, { Container } from 'next/app'
|
||||
|
||||
class MyApp extends App {
|
||||
render() {
|
||||
const { Component, pageProps } = this.props
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<style global jsx>{`
|
||||
@media (max-width: 31.25em) {
|
||||
:root {
|
||||
--smaller-font-size: 0.9rem;
|
||||
--normal-font-size: 1rem;
|
||||
--bigger-font-size: 1.1rem;
|
||||
--big-font-size: 1.3rem;
|
||||
--biggest-font-size: 2rem;
|
||||
|
||||
--font-size-h1: 1.75rem;
|
||||
--font-size-h2: 1.5rem;
|
||||
--font-size-h3: 1.25rem;
|
||||
|
||||
--content-thin: 90%;
|
||||
--content-wide: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
.noscroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#__next {
|
||||
height: 100%;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<Component {...pageProps} />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MyApp
|
||||
@@ -63,8 +63,6 @@ export default class MyDocument extends Document {
|
||||
<body className="custom_class">
|
||||
<Main />
|
||||
<NextScript />
|
||||
|
||||
<script src="/static/app.js" />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
|
||||
+10
-11
@@ -12,21 +12,20 @@ const Page = (props) => (
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'thin'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.album.title}</title>
|
||||
</Head>
|
||||
<div className="content content_thin">
|
||||
<Article
|
||||
title={props.album.title}
|
||||
date={props.album.date}
|
||||
shareable={true}
|
||||
>
|
||||
<Html
|
||||
html={props.album.post}
|
||||
/>
|
||||
</Article>
|
||||
</div>
|
||||
<Article
|
||||
title={props.album.title}
|
||||
date={props.album.date}
|
||||
shareable={true}
|
||||
>
|
||||
<Html
|
||||
html={props.album.post}
|
||||
/>
|
||||
</Article>
|
||||
</App>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { connect } from '../../../src/core/db'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import Article from '../../../src/core/Article'
|
||||
import { encodeArticle } from '../../../src/api/articles'
|
||||
|
||||
export default async (req, res) => {
|
||||
await connect()
|
||||
import { encodeArticle } from '../../../src/server/articles'
|
||||
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
|
||||
|
||||
export default withMiddleware(async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const {
|
||||
query: { article: id }
|
||||
} = req;
|
||||
@@ -19,4 +18,4 @@ export default async (req, res) => {
|
||||
error: `Article ${id} not found`
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,10 +1,7 @@
|
||||
import { connect } from '../../../src/core/db'
|
||||
import { findArticles } from '../../../src/api/articles'
|
||||
import { findArticles } from '../../../src/server/articles'
|
||||
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
|
||||
|
||||
export default withMiddleware(async (req, res) => {
|
||||
await connect()
|
||||
|
||||
const { query } = req
|
||||
const page = parseInt(query.page)
|
||||
const pageSize = parseInt(query.pageSize)
|
||||
@@ -16,7 +13,7 @@ export default withMiddleware(async (req, res) => {
|
||||
} else {
|
||||
res.status(404)
|
||||
res.json({
|
||||
error: `Article ${id} not found`
|
||||
error: `Articles not found`
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,11 +1,8 @@
|
||||
import { connect } from '../../src/core/db'
|
||||
import Image from '../../src/core/Image'
|
||||
import { encodeImage } from '../../src/api/image'
|
||||
import { withMiddleware } from '../../src/middlewares/withMiddleware'
|
||||
|
||||
export default withMiddleware(async (req, res) => {
|
||||
await connect()
|
||||
|
||||
const file = req.query.file
|
||||
const image = await Image.findByFile(file)
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { connect } from '../../../src/core/db'
|
||||
import Page from '../../../src/core/Page'
|
||||
import { withMiddleware } from '../../../src/middlewares/withMiddleware'
|
||||
|
||||
export default withMiddleware(async (req, res) => {
|
||||
await connect()
|
||||
|
||||
const items = await Page.find({})
|
||||
|
||||
if (items) {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { connect } from '../../src/core/db'
|
||||
import { findSchedule } from '../../src/core/schedule'
|
||||
import { findSchedule } from '../../src/server/schedule'
|
||||
import { withMiddleware } from '../../src/middlewares/withMiddleware'
|
||||
|
||||
export default withMiddleware(async (req, res) => {
|
||||
await connect()
|
||||
|
||||
const period = req.query.period
|
||||
const semester = req.query.semester
|
||||
const data = await findSchedule(period, semester)
|
||||
const data = await findSchedule(req.collection, period, semester)
|
||||
|
||||
if (data) {
|
||||
res.json(data)
|
||||
|
||||
+11
-12
@@ -15,23 +15,22 @@ const Page = (props) => (
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'thin'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.article.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
<div className={'content content_thin'}>
|
||||
<Article
|
||||
title={props.article.title}
|
||||
date={props.article.date}
|
||||
tags={props.article.tags}
|
||||
shareable={true}
|
||||
>
|
||||
<Html
|
||||
html={props.article.post}
|
||||
/>
|
||||
</Article>
|
||||
</div>
|
||||
<Article
|
||||
title={props.article.title}
|
||||
date={props.article.date}
|
||||
tags={props.article.tags}
|
||||
shareable={true}
|
||||
>
|
||||
<Html
|
||||
html={props.article.post}
|
||||
/>
|
||||
</Article>
|
||||
</App>
|
||||
)
|
||||
|
||||
|
||||
@@ -13,18 +13,17 @@ const Page = (props) => (
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'wide'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
<div className="content content_wide">
|
||||
<ArticleCardList
|
||||
articles={props.articles}
|
||||
prevPage={props.prevPage}
|
||||
nextPage={props.nextPage}
|
||||
/>
|
||||
</div>
|
||||
<ArticleCardList
|
||||
articles={props.articles}
|
||||
prevPage={props.prevPage}
|
||||
nextPage={props.nextPage}
|
||||
/>
|
||||
</App>
|
||||
)
|
||||
|
||||
|
||||
+22
-23
@@ -4,7 +4,7 @@ import { get } from 'lodash'
|
||||
import { App } from '../src/components/App'
|
||||
import { Meta } from '../src/components/Meta'
|
||||
import { CollectiveImage } from '../src/components/CollectiveImage'
|
||||
import PersonCard from '../src/components/PersonCard'
|
||||
import { PersonCard } from '../src/components/PersonCard'
|
||||
import { CardList } from '../src/components/CardList'
|
||||
import menuModel from '../src/models/menu'
|
||||
import { buildMenu } from '../src/lib/menu'
|
||||
@@ -16,35 +16,34 @@ const Page = (props) => (
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'wide'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
|
||||
<div className="content content_semi-wide">
|
||||
{!props.image ? null : (
|
||||
<CollectiveImage
|
||||
data={props.image}
|
||||
style={{
|
||||
marginBottom: 'var(--double-margin)',
|
||||
}}
|
||||
{!props.image ? null : (
|
||||
<CollectiveImage
|
||||
data={props.image}
|
||||
style={{
|
||||
marginBottom: 'var(--double-margin)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CardList
|
||||
items={props.persons}
|
||||
renderItem={(person, index) => (
|
||||
<PersonCard
|
||||
key={index}
|
||||
profile={person}
|
||||
picture={person.picture}
|
||||
url={person.url}
|
||||
name={person.name}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CardList
|
||||
items={props.persons}
|
||||
renderItem={(person, index) => (
|
||||
<PersonCard
|
||||
key={index}
|
||||
profile={person}
|
||||
picture={person.picture}
|
||||
url={person.url}
|
||||
name={person.name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
</App>
|
||||
)
|
||||
|
||||
@@ -56,7 +55,7 @@ Page.getInitialProps = wrapInitialProps(async (ctx) => {
|
||||
const title = 'Преподаватели Шлиссельбургской ДХШ'
|
||||
const imageFile = 'Images/HudozkaCollective2017.jpg'
|
||||
const resImage = await requestGet(createApiUrl(ctx.req, `/api/image?file=${imageFile}`), null)
|
||||
const image = get(resImage, 'data.artifacts.large', null)
|
||||
const image = get(resImage, 'artifacts.large', null)
|
||||
|
||||
return {
|
||||
persons,
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import Head from 'next/head'
|
||||
import { App } from '../../src/components/App'
|
||||
import Document from '../../src/components/Document'
|
||||
import Share from '../../src/components/Share'
|
||||
import { Share } from '../../src/components/Share'
|
||||
import menuModel from '../../src/models/menu'
|
||||
import { buildMenu } from '../../src/lib/menu'
|
||||
import { meta } from '../../src/lib/meta'
|
||||
@@ -14,15 +14,14 @@ const Page = (props) => (
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'wide'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.file.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
<div className="content content_wide">
|
||||
<Document {...props.file} />
|
||||
<Share />
|
||||
</div>
|
||||
<Document {...props.file} />
|
||||
<Share />
|
||||
</App>
|
||||
)
|
||||
|
||||
|
||||
@@ -5,12 +5,19 @@ import { Meta } from '../src/components/Meta'
|
||||
import menuModel from '../src/models/menu'
|
||||
import { buildMenu } from '../src/lib/menu'
|
||||
import { meta } from '../src/lib/meta'
|
||||
import { IDocument } from '../src/types'
|
||||
|
||||
import DocumentList from '../src/components/DocumentList'
|
||||
import { DocumentList } from '../src/components/DocumentList'
|
||||
import { unique } from '../src/utils/common'
|
||||
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
|
||||
import { createApiUrl, requestGet, wrapInitialProps, IResponseItems } from '../src/next-lib'
|
||||
import { NextPage } from 'next'
|
||||
|
||||
function getSorted(documents) {
|
||||
interface IDocumentCategory {
|
||||
name: string
|
||||
items: IDocument[]
|
||||
}
|
||||
|
||||
function getSorted(documents: IDocument[]): IDocumentCategory[] {
|
||||
const compose = (...fns) => value => fns
|
||||
.map(fn => fn)
|
||||
.reverse()
|
||||
@@ -23,7 +30,7 @@ function getSorted(documents) {
|
||||
|
||||
const subtract = (a, b) => a - b
|
||||
|
||||
const uniqueCategories = unique(i => i.category)
|
||||
const uniqueCategories = unique<IDocument, string>(i => i.category)
|
||||
|
||||
const priority = {
|
||||
'Основные документы': [
|
||||
@@ -53,13 +60,16 @@ function getSorted(documents) {
|
||||
'Документы для поступления',
|
||||
]
|
||||
|
||||
const sortBy = fn => (a, b) => [a, b]
|
||||
.map(fn)
|
||||
.reduce(subtract)
|
||||
function sortBy<T>(fn: (value: T) => number): (a: T, b: T) => number {
|
||||
return (a, b) => [a, b].map(fn).reduce(subtract)
|
||||
}
|
||||
|
||||
const getPriorityFn = (m, list) => value => list.includes(value)
|
||||
? list.indexOf(value)
|
||||
: m
|
||||
function getPriorityFn<T>(m: number, list: T[]): (value: T) => number {
|
||||
return (value: T) => {
|
||||
const has = list.includes(value)
|
||||
return has ? list.indexOf(value) : m
|
||||
}
|
||||
}
|
||||
|
||||
const safeSelect = (d, store, key) => key in store
|
||||
? store[key]
|
||||
@@ -84,33 +94,39 @@ function getSorted(documents) {
|
||||
}], [])
|
||||
}
|
||||
|
||||
const Page = (props) => (
|
||||
interface IProps {
|
||||
collections: IDocumentCategory[]
|
||||
pageUrl: string
|
||||
title: string
|
||||
meta: any
|
||||
}
|
||||
|
||||
const Page: NextPage<IProps> = props => (
|
||||
<App
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'thin'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
|
||||
<div className="content content_thin">
|
||||
{props.collections.map(({ name, items }, index) => (
|
||||
<DocumentList
|
||||
key={index}
|
||||
name={name}
|
||||
documents={items}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{props.collections.map((collection, index) => (
|
||||
<DocumentList
|
||||
key={index}
|
||||
name={collection.name}
|
||||
documents={collection.items}
|
||||
/>
|
||||
))}
|
||||
</App>
|
||||
)
|
||||
|
||||
Page.getInitialProps = wrapInitialProps(async (ctx) => {
|
||||
const pageUrl = '/documents'
|
||||
const res = await requestGet(createApiUrl(ctx.req, '/api/files'), {})
|
||||
const files = res.items || []
|
||||
const res = await requestGet<IResponseItems<IDocument>>(createApiUrl(ctx.req, '/api/files'), { items: [] })
|
||||
const files = res.items
|
||||
const title = 'Документы'
|
||||
const collections = getSorted(files)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react'
|
||||
import Head from 'next/head'
|
||||
import { App } from '../src/components/App'
|
||||
// import Album from '../src/components/Album'
|
||||
import Article from '../src/components/Article'
|
||||
import { Meta } from '../src/components/Meta'
|
||||
import menuModel from '../src/models/menu'
|
||||
import { buildMenu } from '../src/lib/menu'
|
||||
import { splitBy } from '../src/lib/array'
|
||||
import { meta } from '../src/lib/meta'
|
||||
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
|
||||
import { NextPage } from 'next'
|
||||
import { PageHeader } from '../src/components/PageHeader'
|
||||
|
||||
const albumsByYear = splitBy(album => new Date(album.date).getFullYear())
|
||||
|
||||
@@ -63,39 +63,42 @@ const ACollection = ({ title, albums }) => (
|
||||
</div>
|
||||
)
|
||||
|
||||
interface IProps {
|
||||
meta: any
|
||||
pageUrl: string
|
||||
title: string
|
||||
collections: any[]
|
||||
}
|
||||
|
||||
const Page = (props) => (
|
||||
const Page: NextPage<IProps> = props => (
|
||||
<App
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'wide'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
|
||||
<div className="content content_semi-wide">
|
||||
<Article
|
||||
// title={meta.title}
|
||||
title={'Галерея'}
|
||||
>
|
||||
{props.collections.map(([year, albums], index) => (
|
||||
<ACollection
|
||||
key={index}
|
||||
title={year}
|
||||
albums={albums}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Article>
|
||||
</div>
|
||||
<PageHeader
|
||||
title={props.title}
|
||||
/>
|
||||
|
||||
{props.collections.map(([year, albums], index) => (
|
||||
<ACollection
|
||||
key={index}
|
||||
title={year}
|
||||
albums={albums}
|
||||
/>
|
||||
))}
|
||||
</App>
|
||||
)
|
||||
|
||||
Page.getInitialProps = wrapInitialProps(async (ctx) => {
|
||||
const pageUrl = ctx.req.url
|
||||
const res = await requestGet(createApiUrl(ctx.req, `/api/albums`), {})
|
||||
const res = await requestGet<any>(createApiUrl(ctx.req, `/api/albums`), {})
|
||||
const items = res.items || []
|
||||
const albumCollections = albumsByYear(items)
|
||||
const title = 'Галерея'
|
||||
@@ -111,4 +114,4 @@ Page.getInitialProps = wrapInitialProps(async (ctx) => {
|
||||
}
|
||||
})
|
||||
|
||||
export default Page
|
||||
export default Page
|
||||
@@ -8,27 +8,36 @@ import menuModel from '../src/models/menu'
|
||||
import { meta } from '../src/lib/meta'
|
||||
import { buildMenu } from '../src/lib/menu'
|
||||
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
|
||||
import { NextPage } from 'next'
|
||||
|
||||
const Page = (props) => (
|
||||
interface IProps {
|
||||
pageUrl: string
|
||||
title: string
|
||||
meta: any
|
||||
prevPage: number
|
||||
nextPage: number
|
||||
articles: any[]
|
||||
}
|
||||
|
||||
const Page: NextPage<IProps> = props => (
|
||||
<App
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'wide'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
|
||||
<div className="content content_wide">
|
||||
<HudozkaTitle />
|
||||
<HudozkaTitle />
|
||||
|
||||
<ArticleCardList
|
||||
articles={props.articles}
|
||||
prevPage={props.prevPage}
|
||||
nextPage={props.nextPage}
|
||||
/>
|
||||
</div>
|
||||
<ArticleCardList
|
||||
articles={props.articles}
|
||||
prevPage={props.prevPage}
|
||||
nextPage={props.nextPage}
|
||||
/>
|
||||
</App>
|
||||
)
|
||||
|
||||
@@ -36,7 +45,7 @@ Page.getInitialProps = wrapInitialProps(async (ctx) => {
|
||||
const pageUrl = '/'
|
||||
const page = 1
|
||||
const pageSize = ctx.query.pageSize || 15
|
||||
const res = await requestGet(createApiUrl(ctx.req, `/api/articles?page=${page}&pageSize=${pageSize}`), {})
|
||||
const res = await requestGet(createApiUrl(ctx.req, `/api/articles?page=${page}&pageSize=${pageSize}`), {}) as any
|
||||
const articles = res.items || []
|
||||
const nextPage = res.nextPage
|
||||
const prevPage = res.prevPage
|
||||
+10
-11
@@ -1,37 +1,36 @@
|
||||
import React from 'react'
|
||||
import Head from 'next/head'
|
||||
|
||||
import { App } from '../src/components/App'
|
||||
import Page from '../src/components/Page'
|
||||
import { Page } from '../src/components/Page'
|
||||
import menuModel from '../src/models/menu'
|
||||
import { buildMenu } from '../src/lib/menu'
|
||||
import { Meta } from '../src/components/Meta'
|
||||
import { meta } from '../src/lib/meta'
|
||||
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
|
||||
|
||||
const Index = (props) => (
|
||||
const Index = props => (
|
||||
<App
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'thin'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
|
||||
<div className={'content content_thin'}>
|
||||
<Page
|
||||
shareable={true}
|
||||
>
|
||||
{props.content}
|
||||
</Page>
|
||||
</div>
|
||||
<Page
|
||||
showSocialShare={true}
|
||||
>
|
||||
{props.content}
|
||||
</Page>
|
||||
</App>
|
||||
)
|
||||
|
||||
Index.getInitialProps = wrapInitialProps(async (ctx) => {
|
||||
const pageUrl = ctx.req.url
|
||||
// // const pageUrl = '/neighbors/2018'
|
||||
const page = await requestGet(createApiUrl(ctx.req, `/api/page?page=${pageUrl}`), {})
|
||||
const image = page.preview ? page.preview.artifacts.fb : {}
|
||||
|
||||
@@ -48,4 +47,4 @@ Index.getInitialProps = wrapInitialProps(async (ctx) => {
|
||||
}
|
||||
})
|
||||
|
||||
export default Index
|
||||
export default Index
|
||||
+6
-7
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import Head from 'next/head'
|
||||
import { App } from '../src/components/App'
|
||||
import Schedule from '../src/components/Schedule'
|
||||
import Share from '../src/components/Share'
|
||||
import { Schedule } from '../src/components/Schedule'
|
||||
import { Share } from '../src/components/Share'
|
||||
import menuModel from '../src/models/menu'
|
||||
import { buildMenu } from '../src/lib/menu'
|
||||
import { createApiUrl, requestGet, wrapInitialProps } from '../src/next-lib'
|
||||
@@ -12,18 +12,17 @@ const Page = (props) => (
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'wide'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
</Head>
|
||||
|
||||
<div className="content content_wide">
|
||||
{/*<ScheduleList schedules={schedules}/>*/}
|
||||
{/*<ScheduleList schedules={schedules}/>*/}
|
||||
|
||||
<Schedule {...props.schedule} />
|
||||
<Schedule {...props.schedule} />
|
||||
|
||||
<Share />
|
||||
</div>
|
||||
<Share />
|
||||
</App>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react'
|
||||
import axios from 'axios'
|
||||
import Head from 'next/head'
|
||||
import { App } from '../../src/components/App'
|
||||
import Page from '../../src/components/Page'
|
||||
import { Page } from '../../src/components/Page'
|
||||
import { Meta } from '../../src/components/Meta'
|
||||
import menuModel from '../../src/models/menu'
|
||||
import { buildMenu } from '../../src/lib/menu'
|
||||
@@ -14,16 +13,15 @@ const Index = (props) => (
|
||||
menu={buildMenu(props.pageUrl, menuModel)}
|
||||
showAuthor={true}
|
||||
menuPadding={true}
|
||||
layout={'wide'}
|
||||
>
|
||||
<Head>
|
||||
<title>{props.title}</title>
|
||||
<Meta meta={props.meta} />
|
||||
</Head>
|
||||
<div className="content content_thin">
|
||||
<Page shareable={true}>
|
||||
{props.person.post}
|
||||
</Page>
|
||||
</div>
|
||||
<Page shareable={true}>
|
||||
{props.person.post}
|
||||
</Page>
|
||||
</App>
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noEmitOnError": false,
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"jsx": "preserve",
|
||||
"outDir": "./build",
|
||||
"strict": false,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import React from 'react'
|
||||
import Menu from './Menu'
|
||||
import Footer from './Footer'
|
||||
import Comments from './Comments'
|
||||
|
||||
import '../style/style.scss'
|
||||
|
||||
export const App = ({children, menu, showAuthor, menuPadding}) => (
|
||||
<div className="body-wrapper theme-default">
|
||||
<header>
|
||||
<div className="navigation">
|
||||
<div className="navigation__body">
|
||||
<Menu items={menu.items}/>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="body-wrapper__content">
|
||||
<section className={`content content_full ${menuPadding ? 'content--padding-top--menu' : ''}`}>
|
||||
{children}
|
||||
</section>
|
||||
|
||||
<Comments/>
|
||||
</main>
|
||||
|
||||
<Footer showAuthor={showAuthor}
|
||||
address=" г. Шлиссельбург ул. 18 января д. 3"
|
||||
telephone="+7 (81362) 76-312"
|
||||
email="hudozka@gmail.com"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react'
|
||||
import { Navigation } from '../Navigation'
|
||||
|
||||
const Header: React.FC<any> = props => (
|
||||
<Navigation items={props.menuItems} />
|
||||
)
|
||||
|
||||
export default Header
|
||||
@@ -0,0 +1,56 @@
|
||||
import * as React from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import cx from 'classnames'
|
||||
|
||||
import { Footer } from '../Footer'
|
||||
import Comments from '../Comments'
|
||||
import { useReducedMotion } from '../../hooks/useReducedMotion'
|
||||
|
||||
const Header = dynamic(() => import('./Header'), {
|
||||
ssr: false,
|
||||
})
|
||||
|
||||
import '../../style/style.scss'
|
||||
|
||||
export interface IAppProps {
|
||||
menu: any
|
||||
showAuthor: boolean
|
||||
menuPadding: boolean
|
||||
layout: 'full' | 'wide' | 'thin'
|
||||
}
|
||||
|
||||
export const App: React.FC<IAppProps> = props => {
|
||||
const motionDisabled = useReducedMotion()
|
||||
|
||||
return (
|
||||
<div className="body-wrapper theme-default">
|
||||
<header>
|
||||
<div className="navigation">
|
||||
<div className="navigation__body">
|
||||
<Header
|
||||
menuItems={props.menu.items}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="body-wrapper__content">
|
||||
<section className={cx('content', `content_${props.layout}`, {
|
||||
'content--padding-top--menu': props.menuPadding,
|
||||
'reduced-motion': motionDisabled,
|
||||
})}>
|
||||
{props.children}
|
||||
</section>
|
||||
|
||||
<Comments />
|
||||
</main>
|
||||
|
||||
<Footer
|
||||
showAuthor={props.showAuthor}
|
||||
address=" г. Шлиссельбург ул. 18 января д. 3"
|
||||
telephone="+7 (81362) 76-312"
|
||||
email="hudozka@gmail.com"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import { dateFormat } from '../lib/date'
|
||||
import { Share } from './Share'
|
||||
|
||||
const Tag = ({ children }) => (
|
||||
<li className="ArticleTags-Item">
|
||||
@@ -29,15 +30,6 @@ const Head = ({ date, tags = [], children }) => (
|
||||
</header>
|
||||
)
|
||||
|
||||
const Share = () => (
|
||||
<div className="likely">
|
||||
<div className="vkontakte">Поделиться</div>
|
||||
<div className="facebook">Поделиться</div>
|
||||
<div className="telegram">Отправить</div>
|
||||
<div className="twitter">Твитнуть</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const Article = ({ children, date, tags, title, shareable }) => (
|
||||
<article className="Article">
|
||||
<Head
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import * as React from 'react'
|
||||
import { dateFormat } from '../../lib/date'
|
||||
|
||||
export const Date: React.FC<{ children: string }> = ({ children }) => (
|
||||
<p>
|
||||
<style jsx>{`
|
||||
p {
|
||||
color: #ccc;
|
||||
font-size: small;
|
||||
}
|
||||
`}</style>
|
||||
{dateFormat(children)}
|
||||
</p>
|
||||
)
|
||||
@@ -1,13 +1,20 @@
|
||||
import React from 'react'
|
||||
import { Image } from './Image'
|
||||
import { dateFormat } from '../lib/date'
|
||||
import * as React from 'react'
|
||||
import cx from 'classnames'
|
||||
import { Date } from './Date'
|
||||
|
||||
const Date = ({ children }) => (
|
||||
<p className="date">{dateFormat(children)}</p>
|
||||
)
|
||||
interface IImage {
|
||||
alt: string
|
||||
src: string
|
||||
srcSet: Array<{ url: string, density: number }>
|
||||
}
|
||||
|
||||
export const ArticleCard = ({ article }) => (
|
||||
<article className="ArticleCard">
|
||||
export interface IArticleCardProps {
|
||||
article: any
|
||||
preview?: IImage
|
||||
}
|
||||
|
||||
export const ArticleCard: React.FC<IArticleCardProps> = ({ article, ...props }) => (
|
||||
<section className="ArticleCard">
|
||||
<style jsx>{`
|
||||
.ArticleCard {
|
||||
--article-card-width: 30%;
|
||||
@@ -19,8 +26,8 @@ export const ArticleCard = ({ article }) => (
|
||||
|
||||
margin: var(--article-card-margin);
|
||||
width: var(--article-card-width);
|
||||
min-width: 10em;
|
||||
max-width: 30em;
|
||||
min-width: 15em;
|
||||
max-width: 20em;
|
||||
|
||||
border-radius: 10px;
|
||||
|
||||
@@ -29,8 +36,8 @@ export const ArticleCard = ({ article }) => (
|
||||
}
|
||||
|
||||
.ArticleCard > a {
|
||||
display: block;
|
||||
position: relative;
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ArticleCard-image {
|
||||
@@ -64,11 +71,6 @@ export const ArticleCard = ({ article }) => (
|
||||
padding: .75em 1em;
|
||||
}
|
||||
|
||||
.ArticleCard .date {
|
||||
color: #ccc;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.ArticleCard:hover img {
|
||||
width: 105%;
|
||||
height: 105%;
|
||||
@@ -78,20 +80,14 @@ export const ArticleCard = ({ article }) => (
|
||||
}
|
||||
|
||||
// Tablet
|
||||
|
||||
@media screen and (min-width: var(--tablet-min-width)) and (max-width: var(--tablet-max-width)) {
|
||||
.ArticleCard {
|
||||
--article-card-image-height: 12em;
|
||||
}
|
||||
|
||||
@media screen and (orientation: portrait) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mobile
|
||||
|
||||
@media (max-width: var(--mobile-width)) {
|
||||
@media (max-width: 31.25em) {
|
||||
.ArticleCard {
|
||||
--article-card-width: 100%;
|
||||
--article-card-margin: var(--single-margin) 0;
|
||||
@@ -101,12 +97,15 @@ export const ArticleCard = ({ article }) => (
|
||||
|
||||
<a className="invisible" href={article.url}>
|
||||
<div className="ArticleCard-image">
|
||||
{!article.preview ? null : (
|
||||
<Image
|
||||
data={article.preview}
|
||||
alt={article.title}
|
||||
opa={false}
|
||||
/>
|
||||
{!props.preview ? null : (
|
||||
<picture>
|
||||
<img
|
||||
className={cx({ opa: false })}
|
||||
alt={props.preview.alt}
|
||||
src={props.preview.src}
|
||||
srcSet={props.preview.srcSet.map(({ url, density }) => `${url} ${density}x`).join(' ')}
|
||||
/>
|
||||
</picture >
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -114,9 +113,11 @@ export const ArticleCard = ({ article }) => (
|
||||
{article.title}
|
||||
|
||||
{!article.date ? null : (
|
||||
<Date>{article.date}</Date>
|
||||
<Date>
|
||||
{article.date}
|
||||
</Date>
|
||||
)}
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
</section>
|
||||
)
|
||||
@@ -3,18 +3,36 @@ import Paginator from './Paginator'
|
||||
import { ArticleCard } from './ArticleCard'
|
||||
|
||||
export const ArticleCardList = ({ articles, nextPage, prevPage }) => (
|
||||
<div className="ArticleCardList">
|
||||
<div>
|
||||
<style jsx>{`
|
||||
.body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{!prevPage ? null : (
|
||||
<Paginator url={`/articles/${prevPage}`} type="top" />
|
||||
)}
|
||||
|
||||
<div className="ArticleCardList-body">
|
||||
{articles.map((article, i) => (
|
||||
<ArticleCard
|
||||
key={i}
|
||||
article={article}
|
||||
/>
|
||||
))}
|
||||
<div className={'body'}>
|
||||
{articles.map((article, i) => {
|
||||
const preview = {
|
||||
alt: article.title,
|
||||
src: article.preview.src,
|
||||
srcSet: article.preview.set,
|
||||
}
|
||||
|
||||
return (
|
||||
<ArticleCard
|
||||
key={i}
|
||||
article={article}
|
||||
preview={preview}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{!nextPage ? null : (
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
import React from 'react'
|
||||
import Image from './Image'
|
||||
import { Image } from './Image'
|
||||
|
||||
export const CollectiveImage = (props) => (
|
||||
<div
|
||||
className="CollectiveImage"
|
||||
style={props.style}
|
||||
>
|
||||
<style jsx>{`
|
||||
.CollectiveImage {
|
||||
margin - bottom: var(--double-margin);
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<Image
|
||||
data={props.data}
|
||||
src={props.data.src}
|
||||
alt={props.data.alt}
|
||||
set={props.data.set}
|
||||
opa={false}
|
||||
style={{
|
||||
marginBottom: 'var(--double-margin)',
|
||||
}}
|
||||
imgStyle={{
|
||||
width: '100%',
|
||||
borderRadius: '10px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -1,18 +0,0 @@
|
||||
const React = require('react')
|
||||
const Document = require('./DocumentListItem')
|
||||
|
||||
const DocumentList = ({documents, name}) => (
|
||||
<div className="documents-container">
|
||||
<div className="documents-container__head">
|
||||
<h2>{name}</h2>
|
||||
</div>
|
||||
|
||||
<div className="documents-container__body">
|
||||
{documents.map((d, i) => (
|
||||
<Document key={i} {...d}/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
module.exports = DocumentList
|
||||
@@ -0,0 +1,22 @@
|
||||
import * as React from 'react'
|
||||
import { IDocument } from '../../types'
|
||||
import { DocumentListItem } from '../DocumentListItem'
|
||||
|
||||
export interface IDocumentListProps {
|
||||
documents: IDocument[]
|
||||
name: string
|
||||
}
|
||||
|
||||
export const DocumentList: React.FC<IDocumentListProps> = props => (
|
||||
<div className="documents-container">
|
||||
<div className="documents-container__head">
|
||||
<h2>{props.name}</h2>
|
||||
</div>
|
||||
|
||||
<div className="documents-container__body">
|
||||
{props.documents.map((d, i) => (
|
||||
<DocumentListItem key={i} {...d} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -1,23 +0,0 @@
|
||||
const React = require('react')
|
||||
const {ext, size} = require('../lib/file')
|
||||
const {titleCase} = require('../lib/string')
|
||||
|
||||
const Document = ({title, url, imageUrl, fileUrl, fileName, fileSize}) => (
|
||||
<div className="document-row">
|
||||
<a href={url} className="invisible">
|
||||
<div className="document-row__image">
|
||||
<img src={imageUrl} alt={title}/>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div className="document-row__file">
|
||||
<a href={url}>{titleCase(title)}</a>
|
||||
</div>
|
||||
|
||||
<div className="document-row__file-info">
|
||||
<a href={fileUrl} target="_blank">{ext(fileName)} ({size(fileSize)})</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
module.exports = Document
|
||||
@@ -0,0 +1,30 @@
|
||||
import * as React from 'react'
|
||||
import { ext, size } from '../../lib/file'
|
||||
import { titleCase } from '../../lib/string'
|
||||
|
||||
export interface IDocumentListItemProps {
|
||||
title: string
|
||||
url: string
|
||||
imageUrl: string
|
||||
fileUrl: string
|
||||
fileName: string
|
||||
fileSize: number
|
||||
}
|
||||
|
||||
export const DocumentListItem: React.FC<IDocumentListItemProps> = props => (
|
||||
<div className="document-row">
|
||||
<a href={props.url} className="invisible">
|
||||
<div className="document-row__image">
|
||||
<img src={props.imageUrl} alt={props.title} />
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div className="document-row__file">
|
||||
<a href={props.url}>{titleCase(props.title)}</a>
|
||||
</div>
|
||||
|
||||
<div className="document-row__file-info">
|
||||
<a href={props.fileUrl} target="_blank">{ext(props.fileName)} ({size(props.fileSize)})</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -1,44 +0,0 @@
|
||||
const React = require('react')
|
||||
|
||||
const mailto = email => `mailto:${email}`
|
||||
const currentYear = () => (new Date()).getFullYear()
|
||||
|
||||
const Footer = ({children, address, telephone, email, showAuthor}) => (
|
||||
<footer>
|
||||
<div className="contacts">
|
||||
<div>
|
||||
<h4>Контактная информация</h4>
|
||||
<p>Адрес: {address}</p>
|
||||
<p>Телефон: {telephone}</p>
|
||||
<p>Почта: <a href={mailto(email)}>{email}</a></p>
|
||||
<p>Вконтакте: <a href="https://vk.com/shlisselburghudozka">shlisselburghudozka</a></p>
|
||||
<p>Инстаграм: <a href="https://www.instagram.com/hudozka/">hudozka</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="copyright">
|
||||
<Copyright
|
||||
yearStart={2012}
|
||||
yearEnd={currentYear()}
|
||||
/>
|
||||
{!showAuthor ? null : (
|
||||
<Author/>
|
||||
)}
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
|
||||
const Author = () => (
|
||||
<span>
|
||||
Разработка и поддержка сайта —
|
||||
<a href="http://tmshv.ru?utm_source=artshburg&utm_medium=developer&utm_campaign=tmshv_ru">
|
||||
www.tmshv.ru
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
|
||||
const Copyright = ({yearStart, yearEnd}) => (
|
||||
<span>© {yearStart}—{yearEnd} Шлиссельбургская детская художественная школа</span>
|
||||
)
|
||||
|
||||
module.exports = Footer
|
||||
@@ -0,0 +1,49 @@
|
||||
import * as React from 'react'
|
||||
|
||||
const mailto = email => `mailto:${email}`
|
||||
const currentYear = () => (new Date()).getFullYear()
|
||||
|
||||
export interface IFooterProps {
|
||||
address: string
|
||||
telephone: string
|
||||
email: string
|
||||
showAuthor: boolean
|
||||
}
|
||||
|
||||
export const Footer: React.FC<IFooterProps> = props => (
|
||||
<footer>
|
||||
<div className="contacts">
|
||||
<div>
|
||||
<h4>Контактная информация</h4>
|
||||
<p>Адрес: {props.address}</p>
|
||||
<p>Телефон: {props.telephone}</p>
|
||||
<p>Почта: <a href={mailto(props.email)}>{props.email}</a></p>
|
||||
<p>Вконтакте: <a href="https://vk.com/shlisselburghudozka">shlisselburghudozka</a></p>
|
||||
<p>Инстаграм: <a href="https://www.instagram.com/hudozka/">hudozka</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="copyright">
|
||||
<Copyright
|
||||
yearStart={2012}
|
||||
yearEnd={currentYear()}
|
||||
/>
|
||||
{!props.showAuthor ? null : (
|
||||
<Author />
|
||||
)}
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
|
||||
const Author = () => (
|
||||
<span>
|
||||
Разработка и поддержка сайта —
|
||||
<a href="http://tmshv.ru?utm_source=artshburg&utm_medium=developer&utm_campaign=tmshv_ru">
|
||||
www.tmshv.ru
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
|
||||
const Copyright = ({ yearStart, yearEnd }) => (
|
||||
<span>© {yearStart}—{yearEnd} Шлиссельбургская детская художественная школа</span>
|
||||
)
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
export const HudozkaTitle = () => (
|
||||
<div className="HudozkaTitle">
|
||||
<p>МБУДО</p>
|
||||
<h1>Шлиссельбургская детская художественная школа</h1>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
import * as React from 'react'
|
||||
|
||||
export const HudozkaTitle: React.FC = React.memo(() => (
|
||||
<div>
|
||||
<style jsx>{`
|
||||
div {
|
||||
--padding: var(--double-margin) 0;
|
||||
|
||||
padding: var(--padding);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--text-color-second);
|
||||
margin-top: var(--single-margin);
|
||||
font-size: var(--font-size-second);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: var(--single-margin);
|
||||
}
|
||||
|
||||
@media (max-width: 31.25em) {
|
||||
div {
|
||||
--padding: 0 0 var(--double-margin);
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<p>МБУДО</p>
|
||||
<h1>Шлиссельбургская <br /> детская художественная школа</h1>
|
||||
</div>
|
||||
))
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react'
|
||||
import className from 'classnames'
|
||||
|
||||
export const Image = ({data, alt, opa = true}) => (
|
||||
<picture>
|
||||
<style jsx>{`
|
||||
picture img {
|
||||
display: block;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<img
|
||||
className={className({ opa })}
|
||||
alt={alt}
|
||||
src={data.src}
|
||||
srcSet={data.set.map(({url, density}) => `${url} ${density}x`)}
|
||||
/>
|
||||
</picture >
|
||||
)
|
||||
|
||||
export default Image
|
||||
@@ -0,0 +1,40 @@
|
||||
import * as React from 'react'
|
||||
import cx from 'classnames'
|
||||
|
||||
export interface IImageProps {
|
||||
style?: React.CSSProperties
|
||||
imgStyle?: React.CSSProperties
|
||||
src: string
|
||||
alt: string
|
||||
opa: boolean
|
||||
set: Array<{
|
||||
url: string
|
||||
density: number
|
||||
}>
|
||||
}
|
||||
|
||||
export const Image: React.FC<IImageProps> = React.memo(props => {
|
||||
const srcSet = props.set
|
||||
.map(({ url, density }) => `${url} ${density}x`)
|
||||
.join(' ')
|
||||
|
||||
return (
|
||||
<picture style={props.style}>
|
||||
<style jsx>{`
|
||||
picture img {
|
||||
display: block;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<img
|
||||
style={props.imgStyle}
|
||||
className={cx({
|
||||
opa: props.opa
|
||||
})}
|
||||
alt={props.alt}
|
||||
src={props.src}
|
||||
srcSet={srcSet}
|
||||
/>
|
||||
</picture >
|
||||
)
|
||||
})
|
||||
@@ -1,58 +0,0 @@
|
||||
const React = require('react')
|
||||
const getPathWithNoTrailingSlas = require('../lib/url').getPathWithNoTrailingSlash
|
||||
|
||||
const hasSubmenu = item => !!item.items
|
||||
const doDrawSubmenu = item => false //item.active && hasSubmenu(item)
|
||||
const itemUrl = url => getPathWithNoTrailingSlas(url)
|
||||
|
||||
const selectedClassName = flag => flag
|
||||
? 'selected'
|
||||
: ''
|
||||
|
||||
const hmenuCurClassName = flag => flag
|
||||
? 'HMenu__item--cur'
|
||||
: ''
|
||||
|
||||
const MenuItem = ({active, text, url}) => (active
|
||||
? <span>{text}</span>
|
||||
: <a href={itemUrl(url)}>{text}</a>
|
||||
)
|
||||
|
||||
const MenuToggle = () => (
|
||||
<div className="HMenu__toggle">
|
||||
<a href="#"></a>
|
||||
</div>
|
||||
)
|
||||
|
||||
const Menu = ({items}) => {
|
||||
const content = items.map((item, index) => (
|
||||
<li key={index}
|
||||
className={`HMenu__item ${hmenuCurClassName(item.highlighted)} ${item.color} ${selectedClassName(item.highlighted)}`}
|
||||
>
|
||||
<MenuItem {...item}/>
|
||||
|
||||
{!doDrawSubmenu(item) ? null : (
|
||||
<Submenu key={index} items={item.items}/>
|
||||
)}
|
||||
</li>
|
||||
))
|
||||
return (
|
||||
<menu className="main-menu HMenu" data-toggle-width="1024">
|
||||
<MenuToggle/>
|
||||
|
||||
{content}
|
||||
</menu>
|
||||
)
|
||||
}
|
||||
|
||||
const Submenu = ({items}) => (
|
||||
<menu className="main-menu__submenu">
|
||||
{items.map((item, index) => (
|
||||
<li key={index} className={`${item.color} ${selectedClassName(item.active)}`}>
|
||||
<MenuItem {...item}/>
|
||||
</li>
|
||||
))}
|
||||
</menu>
|
||||
)
|
||||
|
||||
module.exports = Menu
|
||||
@@ -0,0 +1,33 @@
|
||||
import * as React from 'react'
|
||||
|
||||
export interface IMenuItemProps {
|
||||
active: boolean
|
||||
text: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export const MenuItem: React.FC<IMenuItemProps> = ({ active, text, url }) => (
|
||||
<>
|
||||
<style jsx>{`
|
||||
a {
|
||||
color: var(--dark-color);
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: var(--dark-color);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
border-color: var(--dark-color);
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{active
|
||||
? (
|
||||
<span>{text}</span>
|
||||
) : (
|
||||
<a href={url}>{text}</a>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
@@ -0,0 +1,125 @@
|
||||
import * as React from 'react'
|
||||
import cx from 'classnames'
|
||||
import { getPathWithNoTrailingSlash } from '../../lib/url'
|
||||
import { MenuItem } from './MenuItem'
|
||||
|
||||
const itemUrl = (url: string) => getPathWithNoTrailingSlash(url)
|
||||
|
||||
export interface IMenuProps {
|
||||
layout: 'desktop' | 'mobile'
|
||||
items: any[]
|
||||
}
|
||||
|
||||
export const Menu: React.FC<IMenuProps> = props => (
|
||||
<div className={cx('menu', props.layout)}>
|
||||
<style jsx>{`
|
||||
.menu {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.desktop {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.mobile {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
li{
|
||||
position: relative;
|
||||
border-top: var(--menu-colored-mark-thickness) solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
li.desktop {
|
||||
padding: 0.3em 0.2em 0;
|
||||
margin-right: 1em;
|
||||
|
||||
font-size: var(--normal-font-size);
|
||||
}
|
||||
|
||||
li.mobile {
|
||||
padding: 0.3em 0.5em;
|
||||
margin-bottom: var(--half-margin);
|
||||
|
||||
font-size: 1.25em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
li.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
li.desktop.selected.blue {
|
||||
border-top-color: var(--blue-color);
|
||||
}
|
||||
|
||||
li.desktop.selected.orange {
|
||||
border-top-color: var(--orange-color);
|
||||
}
|
||||
|
||||
li.desktop.selected.green {
|
||||
border-top-color: var(--green-color);
|
||||
}
|
||||
|
||||
li.desktop.selected.yellow {
|
||||
border-top-color: var(--yellow-color);
|
||||
}
|
||||
|
||||
li.desktop.selected.pink {
|
||||
border-top-color: var(--pink-color);
|
||||
}
|
||||
|
||||
li.desktop.selected.red {
|
||||
border-top-color: var(--red-color);
|
||||
}
|
||||
|
||||
li.mobile.selected.blue {
|
||||
background-color: var(--blue-color);
|
||||
}
|
||||
|
||||
li.mobile.selected.orange {
|
||||
background-color: var(--orange-color);
|
||||
}
|
||||
|
||||
li.mobile.selected.green {
|
||||
background-color: var(--green-color);
|
||||
}
|
||||
|
||||
li.mobile.selected.yellow {
|
||||
background-color: var(--yellow-color);
|
||||
}
|
||||
|
||||
li.mobile.selected.pink {
|
||||
background-color: var(--pink-color);
|
||||
}
|
||||
|
||||
li.mobile.selected.red {
|
||||
background-color: var(--red-color);
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{props.items.map((item, index) => (
|
||||
<li key={index}
|
||||
className={cx(
|
||||
props.layout,
|
||||
item.color, {
|
||||
selected: item.highlighted,
|
||||
})}
|
||||
>
|
||||
<MenuItem
|
||||
url={itemUrl(item.url)}
|
||||
text={item.text}
|
||||
active={item.active}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,79 @@
|
||||
import * as React from 'react'
|
||||
import cx from 'classnames'
|
||||
|
||||
export interface IMenuToggleProps {
|
||||
style?: React.CSSProperties
|
||||
open: boolean
|
||||
position: 'left' | 'right'
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
export const MenuToggle: React.FC<IMenuToggleProps> = props => {
|
||||
return (
|
||||
<div
|
||||
className={cx(props.position, {
|
||||
open: props.open,
|
||||
close: !props.open,
|
||||
})}
|
||||
style={props.style}
|
||||
>
|
||||
<style jsx>{`
|
||||
div {
|
||||
--hmenu-margin: 6px;
|
||||
--hmenu-icon-width: 32px;
|
||||
--hmenu-icon-height: 32px;
|
||||
|
||||
position: absolute;
|
||||
height: var(--hmenu-icon-height);
|
||||
margin: var(--hmenu-margin);
|
||||
}
|
||||
|
||||
div.right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
div.left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
|
||||
width: var(--hmenu-icon-width);
|
||||
height: var(--hmenu-icon-height);
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.open a {
|
||||
background-image: url(/static/graphics/hmenu-close-b.png);
|
||||
}
|
||||
|
||||
div.open a:hover {
|
||||
background-image: url(/static/graphics/hmenu-close-hover-b.png);
|
||||
}
|
||||
|
||||
div.close a {
|
||||
background-image: url(/static/graphics/hmenu-b.png);
|
||||
}
|
||||
|
||||
div.close a:hover {
|
||||
background-image: url(/static/graphics/hmenu-hover-b.png);
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<a
|
||||
href="#"
|
||||
onClick={props.onClick}
|
||||
></a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import { IMeta } from '../../types'
|
||||
|
||||
export const Meta = ({ meta }) => (
|
||||
<React.Fragment>
|
||||
export interface IMetaProps {
|
||||
meta: IMeta
|
||||
}
|
||||
|
||||
export const Meta: React.FC<IMetaProps> = ({ meta }) => (
|
||||
<>
|
||||
{/* Search Engine */}
|
||||
<meta name="description" content={meta.description} />
|
||||
<meta name="image" content={meta.image} />
|
||||
@@ -15,8 +20,8 @@ export const Meta = ({ meta }) => (
|
||||
<meta property="og:title" content={meta.title} />
|
||||
<meta property="og:description" content={meta.description} />
|
||||
<meta property="og:image" content={meta.image} />
|
||||
<meta property="og:image:width" content={meta.imageWidth} />
|
||||
<meta property="og:image:height" content={meta.imageHeight} />
|
||||
<meta property="og:image:width" content={meta.imageWidth.toString()} />
|
||||
<meta property="og:image:height" content={meta.imageHeight.toString()} />
|
||||
<meta property="og:url" content={meta.url} />
|
||||
<meta property="og:site_name" content={meta.siteName} />
|
||||
<meta property="og:locale" content={meta.locale} />
|
||||
@@ -31,5 +36,5 @@ export const Meta = ({ meta }) => (
|
||||
<meta name="twitter:url" content={meta.url} />
|
||||
<meta name="twitter:site" content={meta.twitterSite} />
|
||||
<meta name="twitter:creator" content={meta.twitterCreator} />
|
||||
</React.Fragment>
|
||||
)
|
||||
</>
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
import * as React from 'react'
|
||||
import { MenuToggle } from '../MenuToggle'
|
||||
import { useScreenType } from '../../hooks/useScreenType'
|
||||
import { Menu } from '../Menu'
|
||||
import { Overlay } from '../Overlay'
|
||||
|
||||
export interface INavigationProps {
|
||||
items: any[]
|
||||
}
|
||||
|
||||
export const Navigation: React.FC<INavigationProps> = props => {
|
||||
const isMobile = useScreenType(['phone', 'tablet'])
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const onClick = React.useCallback(
|
||||
() => {
|
||||
setOpen(!open)
|
||||
},
|
||||
[open]
|
||||
)
|
||||
|
||||
return (
|
||||
<nav>
|
||||
<style jsx>{`
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
|
||||
font-size: var(--normal-font-size);
|
||||
list-style: none;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{isMobile ? (
|
||||
<MenuToggle
|
||||
open={open}
|
||||
position={'right'}
|
||||
onClick={onClick}
|
||||
style={{
|
||||
zIndex: 1000001,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Menu
|
||||
layout={'desktop'}
|
||||
items={props.items}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Overlay
|
||||
show={open}
|
||||
onClickOverlay={onClick}
|
||||
>
|
||||
<div style={{
|
||||
width: '50%',
|
||||
margin: '50px auto',
|
||||
}}>
|
||||
<Menu
|
||||
layout={'mobile'}
|
||||
items={props.items}
|
||||
/>
|
||||
</div>
|
||||
</Overlay>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import * as React from 'react'
|
||||
|
||||
export interface ITransitionProps {
|
||||
duration: number
|
||||
extraStyle?: React.CSSProperties
|
||||
show: boolean
|
||||
}
|
||||
|
||||
export const Transition: React.FC<ITransitionProps> = props => {
|
||||
const [mounted, setMounted] = React.useState(props.show)
|
||||
const [opacity, setOpacity] = React.useState(0)
|
||||
const onTransitionEnd = React.useCallback(() => {
|
||||
if (opacity === 0) {
|
||||
setMounted(false)
|
||||
}
|
||||
}, [opacity])
|
||||
const transition = React.useMemo(
|
||||
() => `all ${props.duration}ms`,
|
||||
[props.duration])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!props.show) {
|
||||
setOpacity(0)
|
||||
return
|
||||
}
|
||||
|
||||
setMounted(true)
|
||||
let t = setTimeout(() => {
|
||||
setOpacity(1)
|
||||
}, 16)
|
||||
|
||||
return () => {
|
||||
clearTimeout(t)
|
||||
}
|
||||
}, [props.show])
|
||||
|
||||
return mounted && (
|
||||
<div
|
||||
style={{
|
||||
...props.extraStyle,
|
||||
opacity,
|
||||
transition,
|
||||
}}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as React from 'react'
|
||||
import { Transition } from './Transition'
|
||||
import { useReducedMotion } from '../../hooks/useReducedMotion'
|
||||
|
||||
export interface IOverlayProps {
|
||||
show: boolean
|
||||
onClickOverlay: () => void
|
||||
}
|
||||
|
||||
export const Overlay: React.FC<IOverlayProps> = props => {
|
||||
const motionDisabled = useReducedMotion()
|
||||
const duration = motionDisabled ? 0 : 250
|
||||
|
||||
const onClick = React.useCallback((event: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (event.target === event.currentTarget) {
|
||||
props.onClickOverlay()
|
||||
}
|
||||
}, [props.onClickOverlay])
|
||||
|
||||
React.useEffect(() => {
|
||||
document.body.classList.toggle('noscroll', props.show)
|
||||
}, [props.show])
|
||||
|
||||
return (
|
||||
<Transition
|
||||
duration={duration}
|
||||
show={props.show}
|
||||
extraStyle={{
|
||||
zIndex: 1000000,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={onClick}
|
||||
>
|
||||
<style jsx>{`
|
||||
div {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{props.children}
|
||||
</div>
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react'
|
||||
import Share from './Share'
|
||||
import Html from './Html'
|
||||
|
||||
export default function Page({ children, shareable }) {
|
||||
return (
|
||||
<div className={'Article Article--cloud'}>
|
||||
<div className={'Article-Body'}>
|
||||
<Html
|
||||
html={children}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!shareable ? null : (
|
||||
<Share />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as React from 'react'
|
||||
import { Share } from '../Share'
|
||||
import Html from '../Html'
|
||||
|
||||
export interface IPageProps {
|
||||
showSocialShare: boolean
|
||||
}
|
||||
|
||||
export const Page: React.FC<IPageProps> = props => (
|
||||
<div className={'Article Article--cloud'}>
|
||||
<div className={'Article-Body'}>
|
||||
<Html
|
||||
html={props.children}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!props.showSocialShare ? null : (
|
||||
<Share />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
import * as React from 'react'
|
||||
import { dateFormat } from '../../lib/date'
|
||||
|
||||
export interface IPageHeaderProps {
|
||||
tags?: string[]
|
||||
date?: Date
|
||||
title: string
|
||||
}
|
||||
|
||||
export const PageHeader: React.FC<IPageHeaderProps> = props => (
|
||||
<header className="Article-Head">
|
||||
<h1>{props.title}</h1>
|
||||
|
||||
{!props.date ? null : (
|
||||
<p className="date">{dateFormat(props.date)}</p>
|
||||
)}
|
||||
|
||||
{/* {!(tags.length) ? null : (
|
||||
<TagList tags={tags} />
|
||||
)} */}
|
||||
</header>
|
||||
)
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react'
|
||||
import cx from 'classnames'
|
||||
import { Image } from './Image'
|
||||
|
||||
module.exports = ({ picture, name, url, profile }) => (
|
||||
export const PersonCard = ({ picture, name, url, profile }) => (
|
||||
<div className="PersonCard">
|
||||
<style jsx>{`
|
||||
.PersonCard {
|
||||
@@ -84,7 +85,7 @@ module.exports = ({ picture, name, url, profile }) => (
|
||||
|
||||
// Mobile
|
||||
|
||||
@media (max-width: var(--mobile-width)) {
|
||||
@media (max-width: 31.25em) {
|
||||
.PersonCard {
|
||||
--person-card-width: 100%;
|
||||
}
|
||||
@@ -93,10 +94,14 @@ module.exports = ({ picture, name, url, profile }) => (
|
||||
|
||||
<a className="invisible" href={url}>
|
||||
<div className="PersonCard-Picture">
|
||||
<Image
|
||||
data={picture}
|
||||
opa={false}
|
||||
/>
|
||||
<picture>
|
||||
<img
|
||||
className={cx({ opa: false })}
|
||||
alt={''}
|
||||
src={picture.src}
|
||||
srcSet={picture.set.map(({ url, density }) => `${url} ${density}x`)}
|
||||
/>
|
||||
</picture >
|
||||
</div>
|
||||
|
||||
<div className="PersonCard-Title">
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
const React = require('react')
|
||||
|
||||
const {getDates} = require('../utils/date')
|
||||
const {shortName} = require('../models/collective')
|
||||
|
||||
const now = new Date()
|
||||
const dates = getDates(now)
|
||||
|
||||
const isToday = weekDayIndex => (now.getDay() - 1) === weekDayIndex
|
||||
const weekDay = weekDayIndex => dates[weekDayIndex]
|
||||
const short = shortName
|
||||
|
||||
const todayClass = i => isToday(i)
|
||||
? 'current'
|
||||
: ''
|
||||
|
||||
const HeadDay = ({i, children}) => (
|
||||
<div className={`schedule-table__head-record ${todayClass(i)}`}>
|
||||
<p className="day">{children}</p>
|
||||
<p className="date">{weekDay(i)}</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
const days = [
|
||||
'Понедельник',
|
||||
'Вторник',
|
||||
'Среда',
|
||||
'Четверг',
|
||||
'Пятница',
|
||||
'Суббота',
|
||||
]
|
||||
|
||||
const Schedule = ({groups}) => (
|
||||
<div className="schedule-table">
|
||||
<div className="schedule-table__body">
|
||||
<div className="schedule-table__body-row">
|
||||
<div className="schedule-table__body-group">Гр.</div>
|
||||
|
||||
{days.map((day, index) => (
|
||||
<HeadDay i={index}>{day}</HeadDay>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{groups.map((group, index) => (
|
||||
<GroupRow key={index} {...group}/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const GroupRow = ({name, time, week}) => (
|
||||
<div className="schedule-table__body-row">
|
||||
<div className="schedule-table__body-group">
|
||||
<p className="name">{name}</p>
|
||||
<p className="time">{time}</p>
|
||||
</div>
|
||||
|
||||
{week.map((day, index) => (
|
||||
<Group key={index} day={day} dayIndex={index}/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
const Group = ({day, dayIndex}) => (
|
||||
<div className={`schedule-table__record ${todayClass(dayIndex)}`}>
|
||||
{day.map((lesson, index) => (
|
||||
<Cell key={index} {...lesson}/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
const Cell = ({course, teacher, time}) => (
|
||||
<div className="schedule-table__course">
|
||||
<Lesson {...course}/>
|
||||
<Teacher {...teacher}/>
|
||||
|
||||
{time
|
||||
.map(range => ({range}))
|
||||
.map((range, index) => (
|
||||
<TimeRange key={index} {...range}/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
const Lesson = ({title, url}) => (
|
||||
<p className="lesson">
|
||||
{url
|
||||
? (
|
||||
<a href={url}>
|
||||
{title}
|
||||
</a>
|
||||
)
|
||||
: (
|
||||
<span>
|
||||
{title}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
)
|
||||
|
||||
const Teacher = ({name, url}) => (
|
||||
<p className="teacher">
|
||||
{url
|
||||
? (
|
||||
<a href={url}>
|
||||
{short(name)}
|
||||
</a>
|
||||
)
|
||||
: (
|
||||
<span>
|
||||
{short(name)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
)
|
||||
|
||||
const TimeRange = ({range}) => (
|
||||
<p className="time">{(range || []).join(' — ')}</p>
|
||||
)
|
||||
|
||||
module.exports = Schedule
|
||||
@@ -0,0 +1,126 @@
|
||||
import React from 'react'
|
||||
|
||||
const { getDates } = require('../utils/date')
|
||||
const { shortName } = require('../models/collective')
|
||||
|
||||
const now = new Date()
|
||||
const dates = getDates(now)
|
||||
|
||||
const isToday = weekDayIndex => (now.getDay() - 1) === weekDayIndex
|
||||
const weekDay = weekDayIndex => dates[weekDayIndex]
|
||||
const short = shortName
|
||||
|
||||
const todayClass = i => isToday(i)
|
||||
? 'current'
|
||||
: ''
|
||||
|
||||
const HeadDay = ({ i, children }) => (
|
||||
<div className={`schedule-table__head-record ${todayClass(i)}`}>
|
||||
<p className="day">{children}</p>
|
||||
<p className="date">{weekDay(i)}</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
const days = [
|
||||
'Понедельник',
|
||||
'Вторник',
|
||||
'Среда',
|
||||
'Четверг',
|
||||
'Пятница',
|
||||
'Суббота',
|
||||
]
|
||||
|
||||
export const Schedule = ({ groups }) => (
|
||||
<div className="schedule-table">
|
||||
<div className="schedule-table__body">
|
||||
<div className="schedule-table__body-row">
|
||||
<div className="schedule-table__body-group">Гр.</div>
|
||||
|
||||
{days.map((day, index) => (
|
||||
<HeadDay
|
||||
key={index}
|
||||
i={index}
|
||||
>
|
||||
{day}
|
||||
</HeadDay>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{groups.map((group, index) => (
|
||||
<GroupRow key={index} {...group} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const GroupRow = ({ name, time, week }) => (
|
||||
<div className="schedule-table__body-row">
|
||||
<div className="schedule-table__body-group">
|
||||
<p className="name">{name}</p>
|
||||
<p className="time">{time}</p>
|
||||
</div>
|
||||
|
||||
{week.map((day, index) => (
|
||||
<Group key={index} day={day} dayIndex={index} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
const Group = ({ day, dayIndex }) => (
|
||||
<div className={`schedule-table__record ${todayClass(dayIndex)}`}>
|
||||
{day.map((lesson, index) => (
|
||||
<Cell key={index} {...lesson} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
const Cell = ({ course, teacher, time }) => (
|
||||
<div className="schedule-table__course">
|
||||
<Lesson {...course} />
|
||||
<Teacher {...teacher} />
|
||||
|
||||
{time
|
||||
.map(range => ({ range }))
|
||||
.map((range, index) => (
|
||||
<TimeRange key={index} {...range} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
const Lesson = ({ title, url }) => (
|
||||
<p className="lesson">
|
||||
{url
|
||||
? (
|
||||
<a href={url}>
|
||||
{title}
|
||||
</a>
|
||||
)
|
||||
: (
|
||||
<span>
|
||||
{title}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
)
|
||||
|
||||
const Teacher = ({ name, url }) => (
|
||||
<p className="teacher">
|
||||
{url
|
||||
? (
|
||||
<a href={url}>
|
||||
{short(name)}
|
||||
</a>
|
||||
)
|
||||
: (
|
||||
<span>
|
||||
{short(name)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</p>
|
||||
)
|
||||
|
||||
const TimeRange = ({ range }) => (
|
||||
<p className="time">{(range || []).join(' — ')}</p>
|
||||
)
|
||||
@@ -1,5 +1,5 @@
|
||||
const React = require('react')
|
||||
const text = require('../core/schedule').text
|
||||
const text = require('../server/schedule').text
|
||||
|
||||
const ScheduleList = ({schedules}) => (
|
||||
<div className="schedule-selector">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const React = require('react')
|
||||
import * as React from 'react'
|
||||
|
||||
const Share = () => (
|
||||
export const Share: React.FC = () => (
|
||||
<div className="likely">
|
||||
<div className="vkontakte">Поделиться</div>
|
||||
<div className="facebook">Поделиться</div>
|
||||
@@ -8,5 +8,3 @@ const Share = () => (
|
||||
<div className="twitter">Твитнуть</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
module.exports = Share
|
||||
@@ -1,29 +0,0 @@
|
||||
const React = require('react')
|
||||
const Share = require('./Share')
|
||||
const getHtml = require('../lib/component').getHtml
|
||||
|
||||
const Head = ({url, children}) => (
|
||||
!url
|
||||
? <h1>{children}</h1>
|
||||
: <a href={url}><h2>{children}</h2></a>
|
||||
)
|
||||
|
||||
const TeacherProfile = ({picture, name, url, profile, biography, shareable}) => (
|
||||
<div className="teacher-profile">
|
||||
<div className="teacher-profile__picture">
|
||||
<img src={picture.url}/>
|
||||
</div>
|
||||
|
||||
<div className="teacher-profile__info">
|
||||
<div className="teacher-profile__biography">
|
||||
{getHtml(biography)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!shareable ? null : (
|
||||
<Share/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
module.exports = TeacherProfile
|
||||
@@ -1,65 +0,0 @@
|
||||
const Data = require('./Data')
|
||||
const Image = require('./Image')
|
||||
const ImageArtifactType = require('./ImageArtifactType')
|
||||
const {find, findOne} = require('../lib/store')
|
||||
|
||||
const previewTypes = [
|
||||
ImageArtifactType.SMALL,
|
||||
ImageArtifactType.MEDIUM,
|
||||
ImageArtifactType.BIG,
|
||||
ImageArtifactType.ORIGIN,
|
||||
]
|
||||
const store = () => Data.getStore(Album)
|
||||
|
||||
class Album {
|
||||
static async find(query, options = {}) {
|
||||
const items = await find(store(), query, options)
|
||||
|
||||
return Promise.all(items.map(processAlbum))
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const item = await findOne(store(), {id})
|
||||
|
||||
return !item
|
||||
? null
|
||||
: processAlbum(item)
|
||||
}
|
||||
}
|
||||
|
||||
const previewFromImage = (imgs = []) => imgs.length
|
||||
? imgs[0]
|
||||
: null
|
||||
|
||||
const processAlbum = async album => {
|
||||
let preview = {}
|
||||
// {
|
||||
// data: [
|
||||
// {
|
||||
// url: null,
|
||||
// width: 0,
|
||||
// height: 0,
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
|
||||
const previewImageId = album.preview ? album.preview : previewFromImage(album.images)
|
||||
if (previewImageId) {
|
||||
const image = await Image.findById(previewImageId)
|
||||
if (image) {
|
||||
// const imageArtifacts = image.findArtifact(previewTypes)
|
||||
// if (imageArtifacts) {
|
||||
// preview = imageArtifacts
|
||||
// }
|
||||
preview = image.getPicture(ImageArtifactType.SMALL)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...album,
|
||||
preview,
|
||||
url: `/album/${album.id}`,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Album
|
||||
@@ -0,0 +1,63 @@
|
||||
import Data from './Data'
|
||||
import Image from './Image'
|
||||
import ImageArtifactType from './ImageArtifactType'
|
||||
import { find, findOne } from '../lib/store'
|
||||
|
||||
const previewTypes = [
|
||||
ImageArtifactType.SMALL,
|
||||
ImageArtifactType.MEDIUM,
|
||||
ImageArtifactType.BIG,
|
||||
ImageArtifactType.ORIGIN,
|
||||
]
|
||||
const store = () => Data.getStore('Album')
|
||||
|
||||
export default class Album {
|
||||
static async find(query, options = {}) {
|
||||
const items = await find(store(), query, options as any)
|
||||
|
||||
return Promise.all(items.map(processAlbum))
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const item = await findOne(store(), { id })
|
||||
|
||||
return !item
|
||||
? null
|
||||
: processAlbum(item)
|
||||
}
|
||||
}
|
||||
|
||||
const previewFromImage = (imgs = []) => imgs.length
|
||||
? imgs[0]
|
||||
: null
|
||||
|
||||
const processAlbum = async album => {
|
||||
let preview = {}
|
||||
// {
|
||||
// data: [
|
||||
// {
|
||||
// url: null,
|
||||
// width: 0,
|
||||
// height: 0,
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
|
||||
const previewImageId = album.preview ? album.preview : previewFromImage(album.images)
|
||||
if (previewImageId) {
|
||||
const image = await Image.findById(previewImageId)
|
||||
if (image) {
|
||||
// const imageArtifacts = image.findArtifact(previewTypes)
|
||||
// if (imageArtifacts) {
|
||||
// preview = imageArtifacts
|
||||
// }
|
||||
preview = image.getPicture(ImageArtifactType.SMALL)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...album,
|
||||
preview,
|
||||
url: `/album/${album.id}`,
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
const Data = require('./Data')
|
||||
const Image = require('./Image')
|
||||
const Config = require('./Config')
|
||||
const {find, findOne, total} = require('../lib/store')
|
||||
|
||||
const store = () => Data.getStore(Article)
|
||||
|
||||
function make(C) {
|
||||
return x => new C(x)
|
||||
}
|
||||
|
||||
class Article {
|
||||
static async find(query, options = {}) {
|
||||
const items = await find(store(), query, options)
|
||||
|
||||
return Promise.all(items.map(processArticle))
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const item = await findOne(store(), {id})
|
||||
|
||||
return !item
|
||||
? null
|
||||
: processArticle(item)
|
||||
}
|
||||
|
||||
static async total(query = {}) {
|
||||
return total(store(), query)
|
||||
}
|
||||
|
||||
static async findPinned() {
|
||||
const now = new Date()
|
||||
const query = {until: {$gte: now}}
|
||||
|
||||
return Article.find(query, {
|
||||
sort: {date: -1},
|
||||
})
|
||||
}
|
||||
|
||||
constructor(data) {
|
||||
this.id = data.id
|
||||
this.images = data.images
|
||||
this.post = data.post
|
||||
this.folder = data.folder
|
||||
this.date = data.date
|
||||
this.title = data.title
|
||||
this.until = data.until
|
||||
this.tags = (data.tags || []).map(make(Tag))
|
||||
this.content = data.content
|
||||
this.hash = data.hash
|
||||
this.type = data.type
|
||||
this.origin = data.origin
|
||||
this.file = data.file
|
||||
this.version = data.version
|
||||
this.preview = data.preview
|
||||
|
||||
this.url = `/article/${this.id}`
|
||||
}
|
||||
|
||||
plain() {
|
||||
return {
|
||||
id: this.id,
|
||||
images: this.images,
|
||||
post: this.post,
|
||||
folder: this.folder,
|
||||
date: this.date,
|
||||
title: this.title,
|
||||
until: this.until,
|
||||
tags: this.tags,
|
||||
content: this.content,
|
||||
hash: this.hash,
|
||||
type: this.type,
|
||||
origin: this.origin,
|
||||
file: this.file,
|
||||
version: this.version,
|
||||
preview: this.preview,
|
||||
url: this.url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Tag {
|
||||
constructor(tag) {
|
||||
this.name = tag;
|
||||
}
|
||||
}
|
||||
|
||||
const previewFromImage = (imgs = []) => imgs.length
|
||||
? imgs[0]
|
||||
: null
|
||||
|
||||
async function resolveDefaultPreview() {
|
||||
const config = await Config.findConfig()
|
||||
if (!config) return null
|
||||
|
||||
return Image.findByFile(config.articleCardDefaultPreview)
|
||||
}
|
||||
|
||||
async function processArticle(article) {
|
||||
let preview = null
|
||||
const previewId = article.preview
|
||||
? article.preview
|
||||
: previewFromImage(article.images)
|
||||
|
||||
if (previewId) {
|
||||
preview = await Image.findById(previewId)
|
||||
} else {
|
||||
preview = await resolveDefaultPreview()
|
||||
}
|
||||
|
||||
return new Article({
|
||||
...article,
|
||||
preview,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = Article
|
||||
@@ -0,0 +1,134 @@
|
||||
import Data from './Data'
|
||||
import Image from './Image'
|
||||
import Config from './Config'
|
||||
import { find, findOne, total } from '../lib/store'
|
||||
|
||||
const store = () => Data.getStore<Article>('Article')
|
||||
|
||||
function make(C) {
|
||||
return x => new C(x)
|
||||
}
|
||||
|
||||
export default class Article {
|
||||
static async find(query, options = {}): Promise<Article[]> {
|
||||
const items = await find(store(), query, options as any)
|
||||
|
||||
return Promise.all(items.map(processArticle))
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const item = await findOne(store(), { id })
|
||||
|
||||
return !item
|
||||
? null
|
||||
: processArticle(item)
|
||||
}
|
||||
|
||||
static async total(query = {}) {
|
||||
return total(store(), query)
|
||||
}
|
||||
|
||||
static async findPinned() {
|
||||
const now = new Date()
|
||||
const query = { until: { $gte: now } }
|
||||
|
||||
return Article.find(query, {
|
||||
sort: { date: -1 },
|
||||
})
|
||||
}
|
||||
|
||||
public id: any
|
||||
public images: any
|
||||
public post: any
|
||||
public folder: any
|
||||
public date: any
|
||||
public title: any
|
||||
public until: any
|
||||
public tags: any
|
||||
public content: any
|
||||
public hash: any
|
||||
public type: any
|
||||
public origin: any
|
||||
public file: any
|
||||
public version: any
|
||||
public preview: any
|
||||
public url: any
|
||||
|
||||
constructor(data) {
|
||||
this.id = data.id
|
||||
this.images = data.images
|
||||
this.post = data.post
|
||||
this.folder = data.folder
|
||||
this.date = data.date
|
||||
this.title = data.title
|
||||
this.until = data.until
|
||||
this.tags = (data.tags || []).map(make(Tag))
|
||||
this.content = data.content
|
||||
this.hash = data.hash
|
||||
this.type = data.type
|
||||
this.origin = data.origin
|
||||
this.file = data.file
|
||||
this.version = data.version
|
||||
this.preview = data.preview
|
||||
|
||||
this.url = `/article/${this.id}`
|
||||
}
|
||||
|
||||
plain() {
|
||||
return {
|
||||
id: this.id,
|
||||
images: this.images,
|
||||
post: this.post,
|
||||
folder: this.folder,
|
||||
date: this.date,
|
||||
title: this.title,
|
||||
until: this.until,
|
||||
tags: this.tags,
|
||||
content: this.content,
|
||||
hash: this.hash,
|
||||
type: this.type,
|
||||
origin: this.origin,
|
||||
file: this.file,
|
||||
version: this.version,
|
||||
preview: this.preview,
|
||||
url: this.url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Tag {
|
||||
public name: string
|
||||
|
||||
constructor(tag) {
|
||||
this.name = tag;
|
||||
}
|
||||
}
|
||||
|
||||
const previewFromImage = (imgs = []) => imgs.length
|
||||
? imgs[0]
|
||||
: null
|
||||
|
||||
async function resolveDefaultPreview() {
|
||||
const config = await Config.findConfig()
|
||||
if (!config) return null
|
||||
|
||||
return Image.findByFile(config.articleCardDefaultPreview)
|
||||
}
|
||||
|
||||
async function processArticle(article) {
|
||||
let preview = null
|
||||
const previewId = article.preview
|
||||
? article.preview
|
||||
: previewFromImage(article.images)
|
||||
|
||||
if (previewId) {
|
||||
preview = await Image.findById(previewId)
|
||||
} else {
|
||||
preview = await resolveDefaultPreview()
|
||||
}
|
||||
|
||||
return new Article({
|
||||
...article,
|
||||
preview,
|
||||
})
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
const Data = require('./Data')
|
||||
const {findOne} = require('../lib/store')
|
||||
|
||||
class Config {
|
||||
static get store() {
|
||||
return Data.getStore(Config)
|
||||
}
|
||||
|
||||
static async findConfig() {
|
||||
const config = await Config.findById('settings')
|
||||
return config || new Config({})
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const data = await findOne(Config.store, {id})
|
||||
if (!data) return null
|
||||
|
||||
return new Config(data)
|
||||
}
|
||||
|
||||
constructor({hash, file, ...data}) {
|
||||
this.hash = hash
|
||||
this.file = file
|
||||
this.server = data.server
|
||||
this.articlesPerPage = data.articlesPerPage || 5
|
||||
this.collectiveOrder = data.collectiveOrder || []
|
||||
this.articleCardDefaultPreview = data.articleCardDefaultPreview
|
||||
this.collectiveImage = data.collectiveImage
|
||||
|
||||
const redirect = data.redirect || {}
|
||||
this.redirect = new Map(Object
|
||||
.entries(redirect)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Config
|
||||
@@ -0,0 +1,42 @@
|
||||
import Data from './Data'
|
||||
import { findOne } from '../lib/store'
|
||||
|
||||
const store = () => Data.getStore('Settings')
|
||||
|
||||
export default class Config {
|
||||
static async findConfig() {
|
||||
const config = await Config.findById('settings')
|
||||
return config || new Config({} as any)
|
||||
}
|
||||
|
||||
static async findById(id: string) {
|
||||
const data = await findOne(store(), { id })
|
||||
if (!data) return null
|
||||
|
||||
return new Config(data)
|
||||
}
|
||||
|
||||
public hash: any
|
||||
public file: any
|
||||
public server: any
|
||||
public articlesPerPage: any
|
||||
public collectiveOrder: any
|
||||
public articleCardDefaultPreview: any
|
||||
public collectiveImage: any
|
||||
public redirect: any
|
||||
|
||||
constructor({ hash, file, ...data }) {
|
||||
this.hash = hash
|
||||
this.file = file
|
||||
this.server = data.server
|
||||
this.articlesPerPage = data.articlesPerPage || 5
|
||||
this.collectiveOrder = data.collectiveOrder || []
|
||||
this.articleCardDefaultPreview = data.articleCardDefaultPreview
|
||||
this.collectiveImage = data.collectiveImage
|
||||
|
||||
const redirect = data.redirect || {}
|
||||
this.redirect = new Map(Object
|
||||
.entries(redirect)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
const db = require('./db')
|
||||
|
||||
const store = new Map()
|
||||
|
||||
class Data {
|
||||
static setStore(Class, collection) {
|
||||
store.set(Class, collection)
|
||||
}
|
||||
|
||||
static getStore(Class) {
|
||||
return store.get(Class)
|
||||
}
|
||||
|
||||
static getCollection(name) {
|
||||
return db.collection(name)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Data
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Collection } from 'mongodb'
|
||||
import * as db from './db'
|
||||
|
||||
const store = new Map<string, Collection>()
|
||||
|
||||
export default class Data {
|
||||
static setStore(key: string, collection: Collection) {
|
||||
store.set(key, collection)
|
||||
}
|
||||
|
||||
static getStore<T>(key: string): Collection<T> {
|
||||
return store.get(key)
|
||||
}
|
||||
|
||||
static getCollection(name: string): Collection {
|
||||
return db.collection(name)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
const Data = require('./Data')
|
||||
const Image = require('./Image')
|
||||
const { find, findOne } = require('../lib/store')
|
||||
import Data from './Data'
|
||||
import Image from './Image'
|
||||
import { find, findOne } from '../lib/store'
|
||||
|
||||
const store = () => Data.getStore(Document)
|
||||
const store = () => Data.getStore('Document')
|
||||
|
||||
class Document {
|
||||
export default class Document {
|
||||
static async find(query, options = {}) {
|
||||
const items = await find(store(), query, options)
|
||||
const items = await find(store(), query, options as any)
|
||||
|
||||
return Promise.all(items.map(processDocument))
|
||||
}
|
||||
@@ -18,6 +18,17 @@ class Document {
|
||||
return processDocument(data)
|
||||
}
|
||||
|
||||
public id: any
|
||||
public hash: any
|
||||
public file: any
|
||||
public fileInfo: any
|
||||
public type: any
|
||||
public title: any
|
||||
public category: any
|
||||
public url: any
|
||||
public preview: any
|
||||
public viewUrl: any
|
||||
|
||||
constructor(data) {
|
||||
this.id = data.id
|
||||
this.hash = data.hash
|
||||
@@ -37,6 +48,9 @@ class Document {
|
||||
}
|
||||
|
||||
class DocumentFile {
|
||||
public name: any
|
||||
public size: any
|
||||
|
||||
constructor(data) {
|
||||
this.name = data.name
|
||||
this.size = data.size
|
||||
@@ -52,5 +66,3 @@ const processDocument = async data => {
|
||||
preview,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = Document
|
||||
@@ -1,75 +0,0 @@
|
||||
const Data = require('./Data')
|
||||
const ImageArtifactType = require('./ImageArtifactType')
|
||||
const {findOne} = require('../lib/store')
|
||||
const { mapOf } = require('../server/lib')
|
||||
|
||||
const store = () => Data.getStore(Image)
|
||||
|
||||
class Image {
|
||||
static async findById(id) {
|
||||
const data = await findOne(store(), {_id: id})
|
||||
if (!data) return null
|
||||
|
||||
const artifacts = data.data
|
||||
return new Image({...data, artifacts})
|
||||
}
|
||||
|
||||
static async findByFile(file) {
|
||||
const data = await findOne(store(), {file})
|
||||
if (!data) return null
|
||||
|
||||
const artifacts = data.data
|
||||
return new Image({...data, artifacts})
|
||||
}
|
||||
|
||||
constructor({hash, file, artifacts}) {
|
||||
this.hash = hash
|
||||
this.file = file
|
||||
|
||||
this.artifacts = mapOf(ImageArtifact, artifacts)
|
||||
}
|
||||
|
||||
getArtifact(size) {
|
||||
return this.artifacts.get(size)
|
||||
}
|
||||
|
||||
findArtifact(sizes) {
|
||||
for (let size of sizes) {
|
||||
const a = this.getArtifact(size)
|
||||
if (a) return a
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
getPicture(size) {
|
||||
const retinaSize = ImageArtifactType.retina(size)
|
||||
|
||||
const a = this.getArtifact(size)
|
||||
const a2 = this.getArtifact(retinaSize)
|
||||
const srcset = !a2
|
||||
? []
|
||||
: [{
|
||||
url: a2.url,
|
||||
density: ImageArtifactType.RETINA_DENSITY,
|
||||
}]
|
||||
|
||||
return {
|
||||
src: a.url,
|
||||
width: a.width,
|
||||
height: a.height,
|
||||
set: srcset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImageArtifact {
|
||||
constructor({url, size, width, height}) {
|
||||
this.url = url
|
||||
this.size = size
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Image
|
||||
@@ -0,0 +1,81 @@
|
||||
import Data from './Data'
|
||||
import ImageArtifactType from './ImageArtifactType'
|
||||
import { findOne } from '../lib/store'
|
||||
import { mapOf } from '../server/lib'
|
||||
|
||||
const store = () => Data.getStore('Image')
|
||||
|
||||
export default class Image {
|
||||
static async findById(id) {
|
||||
const data = await findOne(store(), { _id: id })
|
||||
if (!data) return null
|
||||
|
||||
const artifacts = data.data
|
||||
return new Image({ ...data, artifacts })
|
||||
}
|
||||
|
||||
static async findByFile(file: string) {
|
||||
const data = await findOne(store(), { file })
|
||||
if (!data) return null
|
||||
|
||||
const artifacts = data.data
|
||||
return new Image({ ...data, artifacts })
|
||||
}
|
||||
public hash: any
|
||||
public file: any
|
||||
public artifacts: any
|
||||
|
||||
constructor({ hash, file, artifacts }) {
|
||||
this.hash = hash
|
||||
this.file = file
|
||||
|
||||
this.artifacts = mapOf(ImageArtifact, artifacts)
|
||||
}
|
||||
|
||||
getArtifact(size) {
|
||||
return this.artifacts.get(size)
|
||||
}
|
||||
|
||||
findArtifact(sizes) {
|
||||
for (let size of sizes) {
|
||||
const a = this.getArtifact(size)
|
||||
if (a) return a
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
getPicture(size) {
|
||||
const retinaSize = ImageArtifactType.retina(size)
|
||||
|
||||
const a = this.getArtifact(size)
|
||||
const a2 = this.getArtifact(retinaSize)
|
||||
const srcset = !a2
|
||||
? []
|
||||
: [{
|
||||
url: a2.url,
|
||||
density: ImageArtifactType.RETINA_DENSITY,
|
||||
}]
|
||||
|
||||
return {
|
||||
src: a.url,
|
||||
width: a.width,
|
||||
height: a.height,
|
||||
set: srcset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ImageArtifact {
|
||||
public url: any
|
||||
public size: any
|
||||
public width: any
|
||||
public height: any
|
||||
|
||||
constructor({ url, size, width, height }) {
|
||||
this.url = url
|
||||
this.size = size
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
const Data = require('./Data')
|
||||
const Image = require('./Image')
|
||||
const Config = require('./Config')
|
||||
const {find, findOne, total} = require('../lib/store')
|
||||
|
||||
const store = () => Data.getStore(Page)
|
||||
|
||||
class Page {
|
||||
static async find(query, options = {}) {
|
||||
const items = await find(store(), query, options)
|
||||
|
||||
return Promise.all(items.map(processPage))
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const item = await findOne(store(), {id})
|
||||
|
||||
return !item
|
||||
? null
|
||||
: processPage(item)
|
||||
}
|
||||
|
||||
static async findByUrl(url) {
|
||||
const item = await findOne(store(), {url})
|
||||
|
||||
return !item
|
||||
? null
|
||||
: processPage(item)
|
||||
}
|
||||
|
||||
static async total(query = {}) {
|
||||
return total(store(), query)
|
||||
}
|
||||
|
||||
constructor(data) {
|
||||
this.id = data.id
|
||||
this.hash = data.hash
|
||||
this.file = data.file
|
||||
this.url = data.url
|
||||
this.content = data.content
|
||||
this.title = data.title
|
||||
this.data = data.data
|
||||
this.images = data.images
|
||||
this.documents = data.documents
|
||||
this.preview = data.preview
|
||||
}
|
||||
|
||||
plain() {
|
||||
return {
|
||||
id: this.id,
|
||||
images: this.images,
|
||||
documents: this.documents,
|
||||
data: this.data,
|
||||
title: this.title,
|
||||
content: this.content,
|
||||
hash: this.hash,
|
||||
file: this.file,
|
||||
preview: this.preview,
|
||||
url: this.url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const previewFromImage = (imgs = []) => imgs.length
|
||||
? imgs[0]
|
||||
: null
|
||||
|
||||
async function resolveDefaultPreview() {
|
||||
const config = await Config.findConfig()
|
||||
if (!config) return null
|
||||
|
||||
return Image.findByFile(config.articleCardDefaultPreview)
|
||||
}
|
||||
|
||||
async function processPage(article) {
|
||||
let preview = null
|
||||
const previewId = article.preview
|
||||
? article.preview
|
||||
: previewFromImage(article.images)
|
||||
|
||||
if (previewId) {
|
||||
preview = await Image.findById(previewId)
|
||||
} else {
|
||||
preview = await resolveDefaultPreview()
|
||||
}
|
||||
|
||||
return new Page({
|
||||
...article,
|
||||
preview,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = Page
|
||||
@@ -0,0 +1,102 @@
|
||||
import Data from './Data'
|
||||
import { find as storeFind, findOne, total } from '../lib/store'
|
||||
import Image from './Image'
|
||||
import Config from './Config'
|
||||
|
||||
const store = () => Data.getStore('Page')
|
||||
|
||||
export default class Page {
|
||||
static async find(query, options = {}): Promise<Page[]> {
|
||||
const items = await storeFind(store(), query, options as any)
|
||||
|
||||
return Promise.all(items.map(processPage))
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const item = await findOne(store(), { id })
|
||||
|
||||
return !item
|
||||
? null
|
||||
: processPage(item)
|
||||
}
|
||||
|
||||
static async findByUrl(url) {
|
||||
const item = await findOne(store(), { url })
|
||||
|
||||
return !item
|
||||
? null
|
||||
: processPage(item)
|
||||
}
|
||||
|
||||
static async total(query = {}) {
|
||||
return total(store(), query)
|
||||
}
|
||||
|
||||
public id: any
|
||||
public hash: any
|
||||
public file: any
|
||||
public url: string
|
||||
public content: any
|
||||
public title: any
|
||||
public data: any
|
||||
public images: any
|
||||
public documents: any
|
||||
public preview: any
|
||||
|
||||
constructor(data) {
|
||||
this.id = data.id
|
||||
this.hash = data.hash
|
||||
this.file = data.file
|
||||
this.url = data.url
|
||||
this.content = data.content
|
||||
this.title = data.title
|
||||
this.data = data.data
|
||||
this.images = data.images
|
||||
this.documents = data.documents
|
||||
this.preview = data.preview
|
||||
}
|
||||
|
||||
plain() {
|
||||
return {
|
||||
id: this.id,
|
||||
images: this.images,
|
||||
documents: this.documents,
|
||||
data: this.data,
|
||||
title: this.title,
|
||||
content: this.content,
|
||||
hash: this.hash,
|
||||
file: this.file,
|
||||
preview: this.preview,
|
||||
url: this.url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const previewFromImage = (imgs = []) => imgs.length
|
||||
? imgs[0]
|
||||
: null
|
||||
|
||||
async function resolveDefaultPreview() {
|
||||
const config = await Config.findConfig()
|
||||
if (!config) return null
|
||||
|
||||
return Image.findByFile(config.articleCardDefaultPreview)
|
||||
}
|
||||
|
||||
async function processPage(article) {
|
||||
let preview = null
|
||||
const previewId = article.preview
|
||||
? article.preview
|
||||
: previewFromImage(article.images)
|
||||
|
||||
if (previewId) {
|
||||
preview = await Image.findById(previewId)
|
||||
} else {
|
||||
preview = await resolveDefaultPreview()
|
||||
}
|
||||
|
||||
return new Page({
|
||||
...article,
|
||||
preview,
|
||||
})
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
const Data = require('./Data')
|
||||
const Image = require('./Image')
|
||||
const {find, findOne} = require('../lib/store')
|
||||
|
||||
const store = () => Data.getStore(Teacher)
|
||||
|
||||
class Teacher {
|
||||
static async find(query, options = {}) {
|
||||
const items = await find(store(), query, options)
|
||||
|
||||
return Promise.all(items.map(processProfile))
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const data = await findOne(store(), {id})
|
||||
if (!data) return null
|
||||
|
||||
return processProfile(data)
|
||||
}
|
||||
|
||||
constructor(data) {
|
||||
this.id = data.id
|
||||
this.post = data.post
|
||||
this.file = data.file
|
||||
this.edu = data.edu
|
||||
this.picture = data.picture
|
||||
this.preview = data.preview
|
||||
this.diploma = data.diploma
|
||||
this.position = data.position
|
||||
this.name = data.name
|
||||
this.status = data.status
|
||||
this.hash = data.hash
|
||||
|
||||
this.url = `/teacher/${this.id}`
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* "First Middle Last" -> "F. M. Last"
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
shortName() {
|
||||
const f = i => i.replace(/^(.).+/, '$1')
|
||||
const [first, middle, last] = this.splitName()
|
||||
|
||||
return `${f(first)}. ${f(middle)}. ${last}`
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* "First Middle Last" -> ["First", "Middle", "Last"]
|
||||
*
|
||||
* @returns {Array|{index: number, input: string}}
|
||||
*/
|
||||
splitName() {
|
||||
const name = this.name
|
||||
const m = /(.+)\s(.+)\s(.+)/.exec(name)
|
||||
return m.slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
const processProfile = async profile => {
|
||||
const imageId = profile.picture
|
||||
const previewId = profile.preview
|
||||
const picture = await Image.findById(imageId)
|
||||
const preview = await Image.findById(previewId)
|
||||
|
||||
return new Teacher({
|
||||
...profile,
|
||||
picture,
|
||||
preview,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = Teacher
|
||||
@@ -0,0 +1,87 @@
|
||||
import Data from './Data'
|
||||
import Image from './Image'
|
||||
import { find, findOne } from '../lib/store'
|
||||
|
||||
const store = () => Data.getStore('Teacher')
|
||||
|
||||
export default class Teacher {
|
||||
static async find(query, options = {}) {
|
||||
const items = await find(store(), query, options as any)
|
||||
|
||||
return Promise.all(items.map(processProfile))
|
||||
}
|
||||
|
||||
static async findById(id) {
|
||||
const data = await findOne(store(), { id })
|
||||
if (!data) return null
|
||||
|
||||
return processProfile(data)
|
||||
}
|
||||
|
||||
public id: any
|
||||
public post: any
|
||||
public file: any
|
||||
public edu: any
|
||||
public picture: any
|
||||
public preview: any
|
||||
public diploma: any
|
||||
public position: any
|
||||
public name: any
|
||||
public status: any
|
||||
public hash: any
|
||||
public url: any
|
||||
|
||||
constructor(data) {
|
||||
this.id = data.id
|
||||
this.post = data.post
|
||||
this.file = data.file
|
||||
this.edu = data.edu
|
||||
this.picture = data.picture
|
||||
this.preview = data.preview
|
||||
this.diploma = data.diploma
|
||||
this.position = data.position
|
||||
this.name = data.name
|
||||
this.status = data.status
|
||||
this.hash = data.hash
|
||||
|
||||
this.url = `/teacher/${this.id}`
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* "First Middle Last" -> "F. M. Last"
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
shortName() {
|
||||
const f = i => i.replace(/^(.).+/, '$1')
|
||||
const [first, middle, last] = this.splitName()
|
||||
|
||||
return `${f(first)}. ${f(middle)}. ${last}`
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* "First Middle Last" -> ["First", "Middle", "Last"]
|
||||
*
|
||||
* @returns {Array|{index: number, input: string}}
|
||||
*/
|
||||
splitName() {
|
||||
const name = this.name
|
||||
const m = /(.+)\s(.+)\s(.+)/.exec(name)
|
||||
return m.slice(1)
|
||||
}
|
||||
}
|
||||
|
||||
const processProfile = async profile => {
|
||||
const imageId = profile.picture
|
||||
const previewId = profile.preview
|
||||
const picture = await Image.findById(imageId)
|
||||
const preview = await Image.findById(previewId)
|
||||
|
||||
return new Teacher({
|
||||
...profile,
|
||||
picture,
|
||||
preview,
|
||||
})
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import {c} from '../core/db';
|
||||
|
||||
export async function getCollective(query={}) {
|
||||
return await c('collective')
|
||||
.find(query)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a person by it's id
|
||||
*
|
||||
* @param id
|
||||
* @returns {Promise|*}
|
||||
*/
|
||||
export async function getPerson(id){
|
||||
return await c('collective').findOne({id: id});
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
const Data = require('./Data')
|
||||
const Page = require('./Page')
|
||||
const Article = require('./Article')
|
||||
const Album = require('./Album')
|
||||
const Teacher = require('./Teacher')
|
||||
const Document = require('./Document')
|
||||
const Image = require('./Image')
|
||||
const Settings = require('./Config')
|
||||
const getConfig = require('next/config').default
|
||||
|
||||
const { MongoClient, ObjectId } = require('mongodb')
|
||||
|
||||
var db
|
||||
|
||||
async function connect() {
|
||||
if (db) {
|
||||
return db
|
||||
}
|
||||
const mongoUri = getConfig().serverRuntimeConfig.dbUri
|
||||
const connection = await MongoClient.connect(mongoUri)
|
||||
db = init(connection)
|
||||
return db
|
||||
}
|
||||
|
||||
function collection(name) {
|
||||
return db.collection(name)
|
||||
}
|
||||
|
||||
function id(i) {
|
||||
if (typeof i === 'string') return ObjectId(i)
|
||||
else if (i instanceof ObjectId) return i
|
||||
else if (i instanceof Object) return '_id' in i ? ObjectId(i._id) : null
|
||||
return null
|
||||
}
|
||||
|
||||
function init(db) {
|
||||
// Data.setStore('Page', db.collection('pages'))
|
||||
Data.setStore(Page, db.collection('pages'))
|
||||
Data.setStore(Article, db.collection('articles'))
|
||||
Data.setStore(Album, db.collection('albums'))
|
||||
Data.setStore(Image, db.collection('images'))
|
||||
Data.setStore(Teacher, db.collection('collective'))
|
||||
Data.setStore(Document, db.collection('documents'))
|
||||
Data.setStore(Settings, db.collection('settings'))
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
exports.connect = connect
|
||||
exports.collection = collection
|
||||
exports.c = collection
|
||||
exports.id = id
|
||||
@@ -0,0 +1,48 @@
|
||||
import Data from './Data'
|
||||
import Page from './Page'
|
||||
import Article from './Article'
|
||||
const Album = require('./Album')
|
||||
const Teacher = require('./Teacher')
|
||||
const Document = require('./Document')
|
||||
const Image = require('./Image')
|
||||
import Settings from './Config'
|
||||
const getConfig = require('next/config').default
|
||||
|
||||
const { MongoClient, ObjectId } = require('mongodb')
|
||||
|
||||
var db
|
||||
|
||||
export async function connect() {
|
||||
if (db) {
|
||||
return db
|
||||
}
|
||||
const mongoUri = getConfig().serverRuntimeConfig.dbUri
|
||||
const connection = await MongoClient.connect(mongoUri)
|
||||
db = init(connection)
|
||||
return db
|
||||
}
|
||||
|
||||
export function collection(name) {
|
||||
return db.collection(name)
|
||||
}
|
||||
|
||||
export function id(i) {
|
||||
if (typeof i === 'string') return ObjectId(i)
|
||||
else if (i instanceof ObjectId) return i
|
||||
else if (i instanceof Object) return '_id' in i ? ObjectId(i._id) : null
|
||||
return null
|
||||
}
|
||||
|
||||
function init(db) {
|
||||
console.log('INIT DB')
|
||||
|
||||
Data.setStore('Page', db.collection('pages'))
|
||||
Data.setStore('Article', db.collection('articles'))
|
||||
Data.setStore('Album', db.collection('albums'))
|
||||
Data.setStore('Image', db.collection('images'))
|
||||
Data.setStore('Teacher', db.collection('collective'))
|
||||
Data.setStore('Document', db.collection('documents'))
|
||||
Data.setStore('Settings', db.collection('settings'))
|
||||
|
||||
return db
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import useMedia from 'use-media'
|
||||
|
||||
export function useReducedMotion(): boolean {
|
||||
return useMedia('(prefers-reduced-motion: reduce)')
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import * as React from 'react'
|
||||
import useMedia from 'use-media'
|
||||
|
||||
export type Type = 'phone' | 'tablet' | 'laptop' | 'desktop'
|
||||
|
||||
export function useScreenType(types: Type[]): boolean {
|
||||
const isPhone = useMedia('(max-width: 31.25em)')
|
||||
const [match, setMatch] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
setMatch(isPhone && types.includes('phone'))
|
||||
}, [isPhone])
|
||||
|
||||
return match
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react'
|
||||
import {renderToStaticMarkup} from 'react-dom/server'
|
||||
|
||||
import App from '../components/App'
|
||||
|
||||
function renderApp({component, ...options}) {
|
||||
const app = (
|
||||
<App {...options}>
|
||||
{component}
|
||||
</App>
|
||||
)
|
||||
return renderToStaticMarkup(app)
|
||||
}
|
||||
|
||||
exports.renderApp = renderApp
|
||||
@@ -1,18 +0,0 @@
|
||||
const moment = require('moment')
|
||||
moment.locale('ru')
|
||||
|
||||
/**
|
||||
*
|
||||
* Date -> DD.MM.YYYY
|
||||
*
|
||||
* @param date
|
||||
*/
|
||||
exports.dateFormat = date => moment(date).format('L')
|
||||
|
||||
/**
|
||||
*
|
||||
* Date -> Unix time
|
||||
*
|
||||
* @param date
|
||||
*/
|
||||
exports.timestamp = date => date.getTime()
|
||||
@@ -0,0 +1,18 @@
|
||||
import moment from 'moment'
|
||||
moment.locale('ru')
|
||||
|
||||
/**
|
||||
*
|
||||
* Date -> DD.MM.YYYY
|
||||
*
|
||||
* @param date
|
||||
*/
|
||||
export const dateFormat = (date: string | Date) => moment(date).format('L')
|
||||
|
||||
/**
|
||||
*
|
||||
* Date -> Unix time
|
||||
*
|
||||
* @param date
|
||||
*/
|
||||
export const timestamp = (date: Date) => date.getTime()
|
||||
@@ -1,27 +0,0 @@
|
||||
function ext(filname) {
|
||||
let getExt = name => /\.(\w+)$/.exec(name)[1]
|
||||
let defaultExt = ext => ext.toUpperCase()
|
||||
|
||||
const exts = ['png', 'pdf', 'jpg', 'jpeg', 'gif', 'doc']
|
||||
const extd = ['ПНГ', 'ПДФ', 'ЖПГ', 'ЖПГ', 'ГИФ', 'ДОК']
|
||||
|
||||
let ext = getExt(filname)
|
||||
const i = exts.indexOf(ext)
|
||||
|
||||
return i > -1
|
||||
? extd[i]
|
||||
: defaultExt(ext)
|
||||
}
|
||||
|
||||
function size(bytes, precision = 1) {
|
||||
if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) return ''
|
||||
|
||||
const units = ['байт', 'КБ', 'МБ', 'ГБ', 'ТБ', 'ПБ']
|
||||
const number = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||
|
||||
const size = (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision)
|
||||
return `${size} ${units[number]}`
|
||||
}
|
||||
|
||||
exports.ext = ext
|
||||
exports.size = size
|
||||
@@ -0,0 +1,23 @@
|
||||
const getExt = (name: string) => /\.(\w+)$/.exec(name)[1]
|
||||
const defaultExt = (ext: string) => ext.toUpperCase()
|
||||
const exts = ['png', 'pdf', 'jpg', 'jpeg', 'gif', 'doc']
|
||||
const extd = ['ПНГ', 'ПДФ', 'ЖПГ', 'ЖПГ', 'ГИФ', 'ДОК']
|
||||
const units = ['байт', 'КБ', 'МБ', 'ГБ', 'ТБ', 'ПБ']
|
||||
|
||||
export function ext(filname: string): string {
|
||||
const ext = getExt(filname)
|
||||
const i = exts.indexOf(ext)
|
||||
|
||||
return i > -1
|
||||
? extd[i]
|
||||
: defaultExt(ext)
|
||||
}
|
||||
|
||||
export function size(bytes: any, precision = 1): string {
|
||||
if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) return ''
|
||||
|
||||
const number = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||
const size = (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision)
|
||||
|
||||
return `${size} ${units[number]}`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
async function find(collection, query, {sort, skip, limit}) {
|
||||
export async function find(collection, query, {sort, skip, limit}) {
|
||||
const cursor = collection.find(query)
|
||||
|
||||
if (sort) cursor.sort(sort)
|
||||
@@ -8,16 +8,12 @@ async function find(collection, query, {sort, skip, limit}) {
|
||||
return cursor.toArray()
|
||||
}
|
||||
|
||||
async function findOne(collection, query) {
|
||||
export async function findOne(collection, query) {
|
||||
return collection.findOne(query)
|
||||
}
|
||||
|
||||
async function total(collection, query) {
|
||||
export async function total(collection, query) {
|
||||
return collection
|
||||
.find(query)
|
||||
.count()
|
||||
}
|
||||
|
||||
exports.find = find
|
||||
exports.findOne = findOne
|
||||
exports.total = total
|
||||
@@ -0,0 +1,20 @@
|
||||
// import { MongoClient } from 'mongodb';
|
||||
// const { connect, collection } = require('../../src/core/db')
|
||||
import {collection, connect} from '../core/db'
|
||||
|
||||
// const client = new MongoClient(process.env.MONGODB_URI, { useNewUrlParser: true });
|
||||
|
||||
export const withCollection = handler => async (req, res) => {
|
||||
await connect()
|
||||
|
||||
req.collection = collection
|
||||
|
||||
// if (!client.isConnected()) {
|
||||
// return client.connect().then(() => {
|
||||
// req.db = client.db('nextjsmongodbapp');
|
||||
// return handler(req, res);
|
||||
// });
|
||||
// }
|
||||
// req.db = client.db('nextjsmongodbapp');
|
||||
return handler(req, res)
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export const withMiddleware = handler => async (req, res) => {
|
||||
return handler(req, res);
|
||||
};
|
||||
import { withCollection } from './withCollection'
|
||||
|
||||
export const withMiddleware = handler => withCollection(handler)
|
||||
@@ -1,73 +0,0 @@
|
||||
export default class TimelinePostInstagram {
|
||||
constructor(date, image, url, username, userpic, text) {
|
||||
this.type = 'instagram';
|
||||
|
||||
this.date = date;
|
||||
this.image = image;
|
||||
this.url = url;
|
||||
this.username = username;
|
||||
this.userpic = userpic;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
export function fromInstagram1(post) {
|
||||
//var author_id = personByInstagram(team.team, post.data.author);
|
||||
//var name = author_id ? team.short(author_id) : post.data.author;
|
||||
|
||||
const name = post.data.author || 'hudozka';
|
||||
const userpic = 'https://igcdn-photos-a-a.akamaihd.net/hphotos-ak-xaf1/t51.2885-19/11357496_464997000344800_2124591831_a.jpg';
|
||||
return new TimelinePostInstagram(
|
||||
post.date,
|
||||
post.data.image.standard_resolution.url,
|
||||
post.data.url,
|
||||
name,
|
||||
userpic,
|
||||
post.body
|
||||
);
|
||||
}
|
||||
|
||||
export function fromInstagram2(post) {
|
||||
const caption = safe(post => post.data.caption.text, '');
|
||||
const username = safe(post => post.data.user['username'], '');
|
||||
const userpic = safe(post => post.data.user['profile_picture'], '');
|
||||
const image = safe(post => post.data.images['standard_resolution'].url, '');
|
||||
|
||||
return new TimelinePostInstagram(
|
||||
post.date,
|
||||
image(post),
|
||||
post.data.link,
|
||||
username(post),
|
||||
userpic(post),
|
||||
caption(post)
|
||||
);
|
||||
}
|
||||
|
||||
export const types = {
|
||||
'instagram-1': fromInstagram1,
|
||||
'instagram-2': fromInstagram2
|
||||
};
|
||||
|
||||
export function createInstance(post) {
|
||||
if(post.type in types) {
|
||||
let fn = types[post.type];
|
||||
|
||||
try{
|
||||
return fn(post);
|
||||
}catch(e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function safe(fn, defaultValue=null){
|
||||
return function(){
|
||||
try{
|
||||
let i = fn.apply(undefined, arguments);
|
||||
return i ? i : defaultValue;
|
||||
}catch(e){
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,7 @@ export function getPeriod(i){
|
||||
* @param i
|
||||
* @returns {*}
|
||||
*/
|
||||
function getPeriodString(i) {
|
||||
export function getPeriodString(i) {
|
||||
return getPeriod(i)
|
||||
.join('-')
|
||||
}
|
||||
|
||||
exports.getPeriodString = getPeriodString
|
||||
@@ -1,8 +1,12 @@
|
||||
import axios from 'axios'
|
||||
|
||||
export async function requestGet(url, defaultResponse) {
|
||||
export interface IResponseItems<T> {
|
||||
items: T[]
|
||||
}
|
||||
|
||||
export async function requestGet<T>(url: string, defaultResponse: T): Promise<T> {
|
||||
try {
|
||||
const res = await axios.get(url)
|
||||
const res = await axios.get<T>(url)
|
||||
|
||||
return res.data
|
||||
} catch (e) {
|
||||
@@ -19,7 +23,7 @@ export async function requestGet(url, defaultResponse) {
|
||||
export function wrapInitialProps(fn) {
|
||||
return async ctx => {
|
||||
if (process.env.NODE_ENV === 'production' && process.browser) {
|
||||
return __NEXT_DATA__.props.pageProps;
|
||||
return window['__NEXT_DATA__'].props.pageProps;
|
||||
}
|
||||
|
||||
return fn(ctx)
|
||||
@@ -1,8 +1,8 @@
|
||||
const Koa = require('koa')
|
||||
const next = require('next')
|
||||
const { get } = require('koa-route')
|
||||
const { connect } = require('./core/db')
|
||||
const Page = require('./core/Page')
|
||||
import Koa from 'koa'
|
||||
import next from 'next'
|
||||
import { get } from 'koa-route'
|
||||
import { connect } from './core/db'
|
||||
import Page from './core/Page'
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production'
|
||||
const app = next({ dev })
|
||||
@@ -29,7 +29,7 @@ app.prepare().then(async () => {
|
||||
|
||||
server.use(get('*', async ctx => {
|
||||
const url = ctx.req.url
|
||||
|
||||
|
||||
if (pagesUrls.includes(url)) {
|
||||
await app.render(ctx.req, ctx.res, '/next', ctx.query)
|
||||
} else {
|
||||
@@ -43,4 +43,3 @@ app.prepare().then(async () => {
|
||||
console.log(`> Ready on http://localhost:${port}`)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import Article from '../core/Article'
|
||||
import ImageArtifactType from '../core/ImageArtifactType'
|
||||
import { sortBy } from '../utils/sort'
|
||||
import { timestamp } from '../lib/date'
|
||||
import { encodeImage } from './image'
|
||||
import { encodeImage } from '../api/image'
|
||||
|
||||
export const sortArticleByDate = sortBy(
|
||||
x => timestamp(new Date(x.date))
|
||||
@@ -20,7 +20,7 @@ export async function findArticlesNin(nin, skip, limit, sort) {
|
||||
})
|
||||
}
|
||||
|
||||
export async function findArticles(page, pageSize) {
|
||||
export async function findArticles(page: number, pageSize: number) {
|
||||
const limit = pageSize
|
||||
const skip = (page - 1) * pageSize
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
const { collection } = require('../core/db')
|
||||
// import { collection } from '../core/db'
|
||||
|
||||
export async function getCollective(query = {}) {
|
||||
return await collection('collective')
|
||||
.find(query)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a person by it's id
|
||||
*
|
||||
* @param id
|
||||
* @returns {Promise|*}
|
||||
*/
|
||||
export async function getPerson(id) {
|
||||
return await collection('collective').findOne({ id: id });
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
* @param Class
|
||||
* @param object
|
||||
*/
|
||||
exports.mapOf = function mapOf(Class, object) {
|
||||
export function mapOf(Class, object) {
|
||||
return Object
|
||||
.entries(object)
|
||||
.reduce((map, i) => (
|
||||
@@ -1,4 +1,5 @@
|
||||
import {c} from '../core/db'
|
||||
// import {collection} from '../core/db'
|
||||
// const {collection} = require('../core/db')
|
||||
import {createMap} from '../utils/common'
|
||||
import {getCollective} from './collective'
|
||||
|
||||
@@ -10,8 +11,8 @@ const sortBy = require('../lib/sort').sortBy
|
||||
|
||||
const SCHEDULES = 'schedules'
|
||||
|
||||
async function findSchedule(period, semester) {
|
||||
const schedule = await c(SCHEDULES).findOne({
|
||||
export async function findSchedule(collection, period, semester) {
|
||||
const schedule = await collection(SCHEDULES).findOne({
|
||||
$or: [
|
||||
{period: period},
|
||||
{period: schedulePeriod(period)}
|
||||
@@ -23,7 +24,7 @@ async function findSchedule(period, semester) {
|
||||
}
|
||||
|
||||
async function findAllScheduleParams() {
|
||||
const schedules = await c(SCHEDULES)
|
||||
const schedules = await collection(SCHEDULES)
|
||||
.find({}, {
|
||||
period: 1,
|
||||
semester: 1,
|
||||
@@ -142,7 +143,7 @@ async function migrate20to30(schedule) {
|
||||
})
|
||||
}
|
||||
|
||||
exports.findSchedule = findSchedule
|
||||
exports.findAllScheduleParams = findAllScheduleParams
|
||||
exports.findDefaultScheduleParams = findDefaultScheduleParams
|
||||
exports.text = text
|
||||
// exports.findSchedule = findSchedule
|
||||
// exports.findAllScheduleParams = findAllScheduleParams
|
||||
// exports.findDefaultScheduleParams = findDefaultScheduleParams
|
||||
// exports.text = text
|
||||
@@ -1,5 +0,0 @@
|
||||
.ArticleCardList-body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
.HudozkaTitle {
|
||||
--padding: var(--double-margin) 0;
|
||||
|
||||
padding: var(--padding);
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
color: var(--text-color-second);
|
||||
margin-top: var(--single-margin);
|
||||
font-size: var(--font-size-second);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: var(--single-margin);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: var(--mobile-width)) {
|
||||
.HudozkaTitle {
|
||||
--padding: 0 0 var(--double-margin);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
.accessibility-control {
|
||||
width: 100%;
|
||||
|
||||
padding: var(--single-margin);
|
||||
|
||||
span {
|
||||
margin-right: var(--single-margin);
|
||||
}
|
||||
|
||||
span.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__block {
|
||||
margin-bottom: var(--half-margin);
|
||||
}
|
||||
|
||||
ul li {
|
||||
display: inline;
|
||||
margin-right: var(--single-margin);
|
||||
}
|
||||
|
||||
ul {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.accessibility .accessibility-control {
|
||||
a {
|
||||
border-bottom-style: dashed;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
.main-menu {
|
||||
font-size: var(--normal-font-size);
|
||||
|
||||
list-style: none;
|
||||
|
||||
.main-menu__submenu {
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: -1em;
|
||||
|
||||
padding: 0 0.5em 0.5em;
|
||||
|
||||
& > li {
|
||||
display: block;
|
||||
padding: 0.3em 0.2em 0.5em;
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
& > li {
|
||||
display: inline-block;
|
||||
padding: 0.3em 0.2em 0;
|
||||
margin: 0 0.5em;
|
||||
|
||||
position: relative;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--dark-color);
|
||||
border-color: var(--dark-color);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--dark-color);
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
font-weight: bold;
|
||||
border-top: var(--menu-colored-mark-thickness) solid var(--light-color);
|
||||
|
||||
&.blue {
|
||||
border-top: var(--menu-colored-mark-thickness) solid var(--blue-color);
|
||||
}
|
||||
|
||||
&.orange {
|
||||
border-top: var(--menu-colored-mark-thickness) solid var(--orange-color);
|
||||
}
|
||||
|
||||
&.green {
|
||||
border-top: var(--menu-colored-mark-thickness) solid var(--green-color);
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
border-top: var(--menu-colored-mark-thickness) solid var(--yellow-color);
|
||||
}
|
||||
|
||||
&.pink {
|
||||
border-top: var(--menu-colored-mark-thickness) solid var(--pink-color);
|
||||
}
|
||||
|
||||
&.red {
|
||||
border-top: var(--menu-colored-mark-thickness) solid var(--red-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: var(--mobile-width)) {
|
||||
.main-menu{
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
+65
-84
@@ -1,109 +1,90 @@
|
||||
.content {
|
||||
margin: 0 auto 1em;
|
||||
margin: 0 auto 1em;
|
||||
|
||||
&.content_full{
|
||||
width: var(--content-full);
|
||||
}
|
||||
&.content_full {
|
||||
width: var(--content-full);
|
||||
}
|
||||
|
||||
&.content_wide{
|
||||
width: var(--content-wide);
|
||||
}
|
||||
&.content_wide {
|
||||
width: var(--content-wide);
|
||||
}
|
||||
|
||||
&.content_semi-wide{
|
||||
width: var(--content-semi-wide);
|
||||
}
|
||||
&.content_semi-wide {
|
||||
width: var(--content-semi-wide);
|
||||
}
|
||||
|
||||
&.content_thin{
|
||||
width: var(--content-thin);
|
||||
}
|
||||
&.content_thin {
|
||||
width: var(--content-thin);
|
||||
}
|
||||
|
||||
&.content--padding-top--menu{
|
||||
padding-top: var(--menu-thickness);
|
||||
}
|
||||
&.content--padding-top--menu {
|
||||
padding-top: var(--menu-thickness);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Animation on load
|
||||
|
||||
@keyframes smallSlideInFromTop {
|
||||
0% {
|
||||
transform: translateY(-10px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
0% {
|
||||
transform: translateY(-10px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
animation: smallSlideInFromTop 350ms ease-out;
|
||||
animation: smallSlideInFromTop 350ms ease-out;
|
||||
}
|
||||
|
||||
@media screen and (max-width: var(--mobile-width)) {
|
||||
--content-mobile: 95%;
|
||||
|
||||
.content {
|
||||
&.content_wide{
|
||||
width: var(--content-mobile);
|
||||
}
|
||||
|
||||
&.content_semi-wide{
|
||||
width: var(--content-mobile);
|
||||
}
|
||||
|
||||
&.content_thin{
|
||||
width: var(--content-mobile);
|
||||
}
|
||||
|
||||
&.content--padding-top--menu{
|
||||
padding-top: var(--single-margin);
|
||||
}
|
||||
}
|
||||
.content.reduced-motion {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: var(--tablet-min-width)) and (max-width: var(--tablet-max-width)) {
|
||||
$thin-value: 70%;
|
||||
$wide-value: 95%;
|
||||
$semi-wide-value: 80%;
|
||||
|
||||
.content {
|
||||
overflow-x: scroll;
|
||||
|
||||
&.content_wide {
|
||||
width: $wide-value;
|
||||
}
|
||||
|
||||
&.content_semi-wide {
|
||||
width: $semi-wide-value;
|
||||
}
|
||||
|
||||
&.content_thin {
|
||||
width: $thin-value;
|
||||
}
|
||||
|
||||
&.content--padding-top--menu {
|
||||
padding-top: var(--double-margin);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (orientation: portrait) {
|
||||
$wide-value: 100%;
|
||||
$thin-value: 80%;
|
||||
$semi-wide-value: 95%;
|
||||
$thin-value: 70%;
|
||||
$wide-value: 95%;
|
||||
$semi-wide-value: 80%;
|
||||
|
||||
.content {
|
||||
&.content_wide {
|
||||
width: $wide-value;
|
||||
}
|
||||
overflow-x: scroll;
|
||||
|
||||
&.content_semi-wide {
|
||||
width: $semi-wide-value;
|
||||
}
|
||||
&.content_wide {
|
||||
width: $wide-value;
|
||||
}
|
||||
|
||||
&.content_thin {
|
||||
width: $thin-value;
|
||||
}
|
||||
&.content_semi-wide {
|
||||
width: $semi-wide-value;
|
||||
}
|
||||
|
||||
&.content_thin {
|
||||
width: $thin-value;
|
||||
}
|
||||
|
||||
&.content--padding-top--menu {
|
||||
padding-top: var(--double-margin);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (orientation: portrait) {
|
||||
$wide-value: 100%;
|
||||
$thin-value: 80%;
|
||||
$semi-wide-value: 95%;
|
||||
|
||||
.content {
|
||||
&.content_wide {
|
||||
width: $wide-value;
|
||||
}
|
||||
|
||||
&.content_semi-wide {
|
||||
width: $semi-wide-value;
|
||||
}
|
||||
|
||||
&.content_thin {
|
||||
width: $thin-value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
.no-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.HMenu {
|
||||
--hmenu-margin: 6px;
|
||||
--hmenu-icon-width: 32px;
|
||||
--hmenu-icon-height: 32px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
//height: 100%;
|
||||
|
||||
position: relative;
|
||||
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.HMenu a {
|
||||
&:visited {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.HMenu.HMenu--folded {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.HMenu--folded.HMenu--closed {
|
||||
li {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.HMenu--folded.HMenu--open {
|
||||
li {
|
||||
margin: 0 0 var(--single-margin);
|
||||
}
|
||||
}
|
||||
|
||||
.HMenu--folded.HMenu--closed {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.HMenu--folded.HMenu--closed .HMenu__item{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.HMenu--folded .HMenu__item.HMenu__item--cur{
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.HMenu__toggle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: var(--hmenu-icon-height);
|
||||
|
||||
margin: var(--hmenu-margin);
|
||||
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
|
||||
width: var(--hmenu-icon-width);
|
||||
height: var(--hmenu-icon-height);
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.HMenu--folded.HMenu--closed {
|
||||
.HMenu__toggle {
|
||||
a {
|
||||
background-image: url(/graphics/hmenu-b.png);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-image: url(/graphics/hmenu-hover-b.png);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.HMenu--folded.HMenu--open {
|
||||
.HMenu__toggle {
|
||||
a {
|
||||
background-image: url(/graphics/hmenu-close-b.png);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-image: url(/graphics/hmenu-close-hover-b.png);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.HMenu--unfolded {
|
||||
margin-left: 1em;
|
||||
|
||||
li {
|
||||
margin: 0 1em .5em .5em;
|
||||
}
|
||||
|
||||
.HMenu__toggle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.HMenu--folded.HMenu--open {
|
||||
z-index: 100;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
background-color: #ffffff;
|
||||
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
|
||||
overflow-y: auto;
|
||||
|
||||
font-size: 1.5rem;
|
||||
|
||||
/*transition: opacity 0.5s ease-in-out;*/
|
||||
/*-webkit-transition: opacity 0.5s ease-in-out;*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
._HMenu--folded li:last-child {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
@media (max-width: var(--mobile-width)) {
|
||||
.HMenu--folded .HMenu__item {
|
||||
max-width: 10em;
|
||||
}
|
||||
|
||||
.HMenu--folded.HMenu--closed {
|
||||
.HMenu__toggle {
|
||||
a {
|
||||
background-image: url(/graphics/hmenu-b.png);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-image: url(/graphics/hmenu-b.png);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.HMenu--folded.HMenu--open {
|
||||
.HMenu__toggle {
|
||||
a {
|
||||
background-image: url(/graphics/hmenu-close-b.png);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-image: url(/graphics/hmenu-close-b.png);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 123 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user