parent
e6b1550056
commit
cd83807204
363
package-lock.json
generated
363
package-lock.json
generated
|
@ -13,11 +13,14 @@
|
||||||
"@aws-sdk/client-s3": "3.303.0",
|
"@aws-sdk/client-s3": "3.303.0",
|
||||||
"@aws-sdk/lib-storage": "3.303.0",
|
"@aws-sdk/lib-storage": "3.303.0",
|
||||||
"@hocuspocus/provider": "2.0.6",
|
"@hocuspocus/provider": "2.0.6",
|
||||||
|
"@rnwonder/solid-date-picker": "0.7.7",
|
||||||
"@solid-primitives/media": "2.2.3",
|
"@solid-primitives/media": "2.2.3",
|
||||||
|
"@thisbeyond/solid-dnd": "0.7.4",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"formidable": "2.1.1",
|
"formidable": "2.1.1",
|
||||||
"i18next": "22.4.15",
|
"i18next": "22.4.15",
|
||||||
"mailgun.js": "8.2.1",
|
"mailgun.js": "8.2.1",
|
||||||
|
"music-metadata-browser": "2.5.10",
|
||||||
"node-fetch": "3.3.1",
|
"node-fetch": "3.3.1",
|
||||||
"solid-popper": "0.3.0",
|
"solid-popper": "0.3.0",
|
||||||
"typograf": "7.1.0"
|
"typograf": "7.1.0"
|
||||||
|
@ -5612,6 +5615,17 @@
|
||||||
"integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==",
|
"integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@rnwonder/solid-date-picker": {
|
||||||
|
"version": "0.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rnwonder/solid-date-picker/-/solid-date-picker-0.7.7.tgz",
|
||||||
|
"integrity": "sha512-GZDd0zNJNfQoUF18oxdsv6Vo2/7G/zFH2xMrJUg4gBe9tqhT0MLBzAUr+ZRBBmViFVYfanSc2iCLN81xXsMOIg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"solid-js": "^1.6.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sinclair/typebox": {
|
"node_modules/@sinclair/typebox": {
|
||||||
"version": "0.25.24",
|
"version": "0.25.24",
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
|
||||||
|
@ -5827,6 +5841,14 @@
|
||||||
"solid-js": ">=1.4.0"
|
"solid-js": ">=1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@thisbeyond/solid-dnd": {
|
||||||
|
"version": "0.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@thisbeyond/solid-dnd/-/solid-dnd-0.7.4.tgz",
|
||||||
|
"integrity": "sha512-jgV9EtR3gAtVsILG8p1OAGrhHIgnK4W04YxpyLgJRCDKEFYQWuDrMdUe8F5Kc6pcVXlC4IMXr4cB8fS2Ut3/Ow==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"solid-js": "^1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@thisbeyond/solid-select": {
|
"node_modules/@thisbeyond/solid-select": {
|
||||||
"version": "0.14.0",
|
"version": "0.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.14.0.tgz",
|
||||||
|
@ -6269,6 +6291,11 @@
|
||||||
"@tiptap/core": "^2.0.0"
|
"@tiptap/core": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tokenizer/token": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
||||||
|
},
|
||||||
"node_modules/@tootallnate/once": {
|
"node_modules/@tootallnate/once": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||||
|
@ -6975,6 +7002,17 @@
|
||||||
"integrity": "sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA==",
|
"integrity": "sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"dependencies": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/abstract-leveldown": {
|
"node_modules/abstract-leveldown": {
|
||||||
"version": "6.2.3",
|
"version": "6.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
|
||||||
|
@ -8305,6 +8343,14 @@
|
||||||
"upper-case": "^2.0.2"
|
"upper-case": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/content-type": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/convert-source-map": {
|
"node_modules/convert-source-map": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||||
|
@ -8509,7 +8555,6 @@
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
},
|
},
|
||||||
|
@ -9973,6 +10018,14 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/events": {
|
"node_modules/events": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||||
|
@ -10231,6 +10284,22 @@
|
||||||
"node": "^10.12.0 || >=12.0.0"
|
"node": "^10.12.0 || >=12.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/file-type": {
|
||||||
|
"version": "16.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
|
||||||
|
"integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-web-to-node-stream": "^3.0.0",
|
||||||
|
"strtok3": "^6.2.4",
|
||||||
|
"token-types": "^4.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/filelist": {
|
"node_modules/filelist": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||||
|
@ -15960,6 +16029,14 @@
|
||||||
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
|
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/media-typer": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/meow": {
|
"node_modules/meow": {
|
||||||
"version": "9.0.0",
|
"version": "9.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
|
||||||
|
@ -16223,8 +16300,82 @@
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
},
|
||||||
|
"node_modules/music-metadata": {
|
||||||
|
"version": "7.13.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-7.13.4.tgz",
|
||||||
|
"integrity": "sha512-eRRoEMhhYdth2Ws24FmkvIqrtkIBE9sqjHbrRNpkg2Iux3zc37PQKRv2/r/mTtELb7XlB1uWC2UcKKX7BzNMGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tokenizer/token": "^0.3.0",
|
||||||
|
"content-type": "^1.0.5",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"file-type": "^16.5.4",
|
||||||
|
"media-typer": "^1.1.0",
|
||||||
|
"strtok3": "^6.3.0",
|
||||||
|
"token-types": "^4.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/music-metadata-browser": {
|
||||||
|
"version": "2.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/music-metadata-browser/-/music-metadata-browser-2.5.10.tgz",
|
||||||
|
"integrity": "sha512-03UnAmsSJoZZ5kK2BnEnd2zpH8LXRWQ6xlc7akKudhc2d9FT+yAiqapnmOzjW3g4cxxvIsSK5MVBO2Gi+Ymjfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"music-metadata": "^7.13.3",
|
||||||
|
"readable-stream": "^4.3.0",
|
||||||
|
"readable-web-to-node-stream": "^3.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/music-metadata-browser/node_modules/buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/music-metadata-browser/node_modules/readable-stream": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==",
|
||||||
|
"dependencies": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"string_decoder": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mute-stream": {
|
"node_modules/mute-stream": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
|
@ -16845,6 +16996,18 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/peek-readable": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
|
@ -17189,6 +17352,14 @@
|
||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/promise": {
|
"node_modules/promise": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||||
|
@ -17679,6 +17850,21 @@
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-web-to-node-stream": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": "^3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
@ -18812,6 +18998,22 @@
|
||||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
||||||
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
|
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/strtok3": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tokenizer/token": "^0.3.0",
|
||||||
|
"peek-readable": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/style-search": {
|
"node_modules/style-search": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
|
||||||
|
@ -19456,6 +19658,22 @@
|
||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/token-types": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tokenizer/token": "^0.3.0",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/totalist": {
|
"node_modules/totalist": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
|
||||||
|
@ -24799,6 +25017,12 @@
|
||||||
"integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==",
|
"integrity": "sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@rnwonder/solid-date-picker": {
|
||||||
|
"version": "0.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rnwonder/solid-date-picker/-/solid-date-picker-0.7.7.tgz",
|
||||||
|
"integrity": "sha512-GZDd0zNJNfQoUF18oxdsv6Vo2/7G/zFH2xMrJUg4gBe9tqhT0MLBzAUr+ZRBBmViFVYfanSc2iCLN81xXsMOIg==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"@sinclair/typebox": {
|
"@sinclair/typebox": {
|
||||||
"version": "0.25.24",
|
"version": "0.25.24",
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz",
|
||||||
|
@ -24980,6 +25204,12 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"@thisbeyond/solid-dnd": {
|
||||||
|
"version": "0.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@thisbeyond/solid-dnd/-/solid-dnd-0.7.4.tgz",
|
||||||
|
"integrity": "sha512-jgV9EtR3gAtVsILG8p1OAGrhHIgnK4W04YxpyLgJRCDKEFYQWuDrMdUe8F5Kc6pcVXlC4IMXr4cB8fS2Ut3/Ow==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"@thisbeyond/solid-select": {
|
"@thisbeyond/solid-select": {
|
||||||
"version": "0.14.0",
|
"version": "0.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.14.0.tgz",
|
||||||
|
@ -25223,6 +25453,11 @@
|
||||||
"prosemirror-view": "^1.28.2"
|
"prosemirror-view": "^1.28.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@tokenizer/token": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
||||||
|
},
|
||||||
"@tootallnate/once": {
|
"@tootallnate/once": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||||
|
@ -25806,6 +26041,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"requires": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"abstract-leveldown": {
|
"abstract-leveldown": {
|
||||||
"version": "6.2.3",
|
"version": "6.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
|
||||||
|
@ -26788,6 +27031,11 @@
|
||||||
"upper-case": "^2.0.2"
|
"upper-case": "^2.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"content-type": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
|
||||||
|
},
|
||||||
"convert-source-map": {
|
"convert-source-map": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||||
|
@ -26947,7 +27195,6 @@
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
}
|
}
|
||||||
|
@ -28012,6 +28259,11 @@
|
||||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
|
||||||
|
},
|
||||||
"events": {
|
"events": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||||
|
@ -28213,6 +28465,16 @@
|
||||||
"flat-cache": "^3.0.4"
|
"flat-cache": "^3.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"file-type": {
|
||||||
|
"version": "16.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
|
||||||
|
"integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
|
||||||
|
"requires": {
|
||||||
|
"readable-web-to-node-stream": "^3.0.0",
|
||||||
|
"strtok3": "^6.2.4",
|
||||||
|
"token-types": "^4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"filelist": {
|
"filelist": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||||
|
@ -32470,6 +32732,11 @@
|
||||||
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
|
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"media-typer": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="
|
||||||
|
},
|
||||||
"meow": {
|
"meow": {
|
||||||
"version": "9.0.0",
|
"version": "9.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
|
||||||
|
@ -32655,8 +32922,56 @@
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
},
|
||||||
|
"music-metadata": {
|
||||||
|
"version": "7.13.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-7.13.4.tgz",
|
||||||
|
"integrity": "sha512-eRRoEMhhYdth2Ws24FmkvIqrtkIBE9sqjHbrRNpkg2Iux3zc37PQKRv2/r/mTtELb7XlB1uWC2UcKKX7BzNMGA==",
|
||||||
|
"requires": {
|
||||||
|
"@tokenizer/token": "^0.3.0",
|
||||||
|
"content-type": "^1.0.5",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"file-type": "^16.5.4",
|
||||||
|
"media-typer": "^1.1.0",
|
||||||
|
"strtok3": "^6.3.0",
|
||||||
|
"token-types": "^4.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"music-metadata-browser": {
|
||||||
|
"version": "2.5.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/music-metadata-browser/-/music-metadata-browser-2.5.10.tgz",
|
||||||
|
"integrity": "sha512-03UnAmsSJoZZ5kK2BnEnd2zpH8LXRWQ6xlc7akKudhc2d9FT+yAiqapnmOzjW3g4cxxvIsSK5MVBO2Gi+Ymjfw==",
|
||||||
|
"requires": {
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"music-metadata": "^7.13.3",
|
||||||
|
"readable-stream": "^4.3.0",
|
||||||
|
"readable-web-to-node-stream": "^3.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==",
|
||||||
|
"requires": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"string_decoder": "^1.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"mute-stream": {
|
"mute-stream": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
|
@ -33102,6 +33417,11 @@
|
||||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"peek-readable": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="
|
||||||
|
},
|
||||||
"picocolors": {
|
"picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
|
@ -33326,6 +33646,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
|
||||||
|
},
|
||||||
"promise": {
|
"promise": {
|
||||||
"version": "7.3.1",
|
"version": "7.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||||
|
@ -33732,6 +34057,14 @@
|
||||||
"util-deprecate": "^1.0.1"
|
"util-deprecate": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"readable-web-to-node-stream": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
|
||||||
|
"requires": {
|
||||||
|
"readable-stream": "^3.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"readdirp": {
|
"readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
@ -34574,6 +34907,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
||||||
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
|
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
|
||||||
},
|
},
|
||||||
|
"strtok3": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
|
||||||
|
"requires": {
|
||||||
|
"@tokenizer/token": "^0.3.0",
|
||||||
|
"peek-readable": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"style-search": {
|
"style-search": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
|
||||||
|
@ -35068,6 +35410,15 @@
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"token-types": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
|
||||||
|
"requires": {
|
||||||
|
"@tokenizer/token": "^0.3.0",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"totalist": {
|
"totalist": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
|
||||||
|
|
5
public/icons/datepicker.svg
Normal file
5
public/icons/datepicker.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g opacity="0.3">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 5H13V6.6H17V5H19V6.6H21H23V8.6V20.6V22.6H21H9H7V20.6V8.6V6.6H9H11V5ZM17 8.6V9.8H19V8.6H21V11.4H9V8.6H11V9.8H13V8.6H17ZM9 13.4V20.6H21V13.4H9Z" fill="black"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 337 B |
9
public/icons/expand-circle.svg
Normal file
9
public/icons/expand-circle.svg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="insert_link">
|
||||||
|
<circle id="Ellipse 13" opacity="0.5" cx="16" cy="16" r="16" fill="black"/>
|
||||||
|
<g id="Group">
|
||||||
|
<path id="Vector" d="M9 22.9999H13.3243V21.7027H11.2054L15.4 17.5297L14.4704 16.6001L10.2974 20.7947V18.6758H9.00019L9 22.9999Z" fill="white"/>
|
||||||
|
<path id="Vector_2" d="M17.5744 15.3568L21.7041 11.2054V13.3243H23.0013V9H18.677V10.2972H20.7959L16.6445 14.4269L17.5744 15.3568Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 515 B |
10
public/icons/list.svg
Normal file
10
public/icons/list.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="List plus">
|
||||||
|
<g id="Group">
|
||||||
|
<path id="Vector" d="M4.5 2.99988V20.9999H19.5V8.39988L13.875 2.99988H4.5ZM17.625 19.1999H6.375V4.79988H12V10.1999H17.625V19.1999ZM13.8751 8.39988V5.54563L16.8483 8.39988H13.8751Z" fill="#141414"/>
|
||||||
|
<path id="Vector_2" d="M8.25 8.39978H10.125V10.1998H8.25V8.39978Z" fill="#141414"/>
|
||||||
|
<path id="Vector_3" d="M8.25 11.9999H15.75V13.7999H8.25V11.9999Z" fill="#141414"/>
|
||||||
|
<path id="Vector_4" d="M8.25 15.6H15.75V17.4H8.25V15.6Z" fill="#141414"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 586 B |
5
public/icons/pencil-stroke.svg
Normal file
5
public/icons/pencil-stroke.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="bookmark">
|
||||||
|
<path id="Vector" d="M19.0195 4.90048C17.8189 3.69984 15.7378 3.69984 14.4571 4.90048L6.13272 13.2249C6.05265 13.3049 5.97259 13.465 5.97259 13.545L4.0516 18.9079C3.81152 19.5481 4.45181 20.1886 5.09209 19.9484L10.3749 17.9473C10.455 17.9473 10.615 17.7872 10.695 17.7872L19.0194 9.38272C20.3001 8.10213 20.3001 6.10098 19.0194 4.90035L19.0195 4.90048ZM6.13272 17.7873L7.01321 15.2259C7.97378 16.1865 7.6535 15.8662 8.61406 16.8267L6.13272 17.7873ZM17.8989 8.26226L10.0548 16.1064L7.81367 13.8653L15.6578 6.02113C16.2981 5.38085 17.3387 5.38085 17.8989 6.02113C18.5393 6.58137 18.5393 7.62197 17.8989 8.26226V8.26226Z" fill="black"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 760 B |
|
@ -3,13 +3,16 @@
|
||||||
"About myself": "About myself",
|
"About myself": "About myself",
|
||||||
"About the project": "About the project",
|
"About the project": "About the project",
|
||||||
"Add another image": "Add another image",
|
"Add another image": "Add another image",
|
||||||
|
"Add audio": "Add audio",
|
||||||
"Add comment": "Comment",
|
"Add comment": "Comment",
|
||||||
|
"Add cover": "Add cover",
|
||||||
"Add image": "Add image",
|
"Add image": "Add image",
|
||||||
"Add images": "Add images",
|
"Add images": "Add images",
|
||||||
"Add link": "Add link",
|
"Add link": "Add link",
|
||||||
"Add signature": "Add signature",
|
"Add signature": "Add signature",
|
||||||
"Add url": "Add url",
|
"Add url": "Add url",
|
||||||
"Address on Discourse": "Address on Discourse",
|
"Address on Discourse": "Address on Discourse",
|
||||||
|
"Album name": "Название aльбома",
|
||||||
"Alignment center": "Alignment center",
|
"Alignment center": "Alignment center",
|
||||||
"Alignment left": "Alignment left",
|
"Alignment left": "Alignment left",
|
||||||
"Alignment right": "Alignment right",
|
"Alignment right": "Alignment right",
|
||||||
|
@ -18,6 +21,7 @@
|
||||||
"All posts": "All posts",
|
"All posts": "All posts",
|
||||||
"All topics": "All topics",
|
"All topics": "All topics",
|
||||||
"Almost done! Check your email.": "Almost done! Just checking your email.",
|
"Almost done! Check your email.": "Almost done! Just checking your email.",
|
||||||
|
"Artist": "Artist",
|
||||||
"Artworks": "Artworks",
|
"Artworks": "Artworks",
|
||||||
"Audio": "Audio",
|
"Audio": "Audio",
|
||||||
"Author": "Author",
|
"Author": "Author",
|
||||||
|
@ -62,9 +66,12 @@
|
||||||
"Create account from follow": "Create an account to subscribe",
|
"Create account from follow": "Create an account to subscribe",
|
||||||
"Create account from subscribe": "Create an account to subscribe to new publications",
|
"Create account from subscribe": "Create an account to subscribe to new publications",
|
||||||
"Create account from vote": "Create an account to vote",
|
"Create account from vote": "Create an account to vote",
|
||||||
|
"Create gallery": "Create gallery",
|
||||||
"Create post": "Create post",
|
"Create post": "Create post",
|
||||||
|
"Create video": "Create video",
|
||||||
"Date of Birth": "Date of Birth",
|
"Date of Birth": "Date of Birth",
|
||||||
"Delete": "Delete",
|
"Delete": "Delete",
|
||||||
|
"Description": "Description...",
|
||||||
"Discours": "Discours",
|
"Discours": "Discours",
|
||||||
"Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects",
|
"Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects",
|
||||||
"Discours is created with our common effort": "Discours exists because of our common effort",
|
"Discours is created with our common effort": "Discours exists because of our common effort",
|
||||||
|
@ -101,6 +108,7 @@
|
||||||
"Forgot password?": "Forgot your password?",
|
"Forgot password?": "Forgot your password?",
|
||||||
"Forward": "Forward",
|
"Forward": "Forward",
|
||||||
"Full name": "First and last name",
|
"Full name": "First and last name",
|
||||||
|
"Gallery name": "Gallery name",
|
||||||
"Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine": "Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine",
|
"Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine": "Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine",
|
||||||
"Go to main page": "Go to main page",
|
"Go to main page": "Go to main page",
|
||||||
"Group Chat": "Group Chat",
|
"Group Chat": "Group Chat",
|
||||||
|
@ -162,6 +170,7 @@
|
||||||
"My feed": "My feed",
|
"My feed": "My feed",
|
||||||
"My subscriptions": "Subscriptions",
|
"My subscriptions": "Subscriptions",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
|
"New literary work": "New literary work",
|
||||||
"New only": "New only",
|
"New only": "New only",
|
||||||
"New password": "New password",
|
"New password": "New password",
|
||||||
"New stories every day and even more!": "New stories and more are waiting for you every day!",
|
"New stories every day and even more!": "New stories and more are waiting for you every day!",
|
||||||
|
@ -199,6 +208,7 @@
|
||||||
"Profile": "Profile",
|
"Profile": "Profile",
|
||||||
"Profile settings": "Profile settings",
|
"Profile settings": "Profile settings",
|
||||||
"Publications": "Publications",
|
"Publications": "Publications",
|
||||||
|
"Publish Album": "Publish Album",
|
||||||
"Publish Settings": "Publish Settings",
|
"Publish Settings": "Publish Settings",
|
||||||
"Punchline": "Punchline",
|
"Punchline": "Punchline",
|
||||||
"Quit": "Quit",
|
"Quit": "Quit",
|
||||||
|
@ -226,9 +236,12 @@
|
||||||
"Share": "Share",
|
"Share": "Share",
|
||||||
"Short opening": "Short opening",
|
"Short opening": "Short opening",
|
||||||
"Show": "Show",
|
"Show": "Show",
|
||||||
|
"Show lyrics": "Текст песни",
|
||||||
"Social networks": "Social networks",
|
"Social networks": "Social networks",
|
||||||
"Something went wrong, check email and password": "Something went wrong. Check your email and password",
|
"Something went wrong, check email and password": "Something went wrong. Check your email and password",
|
||||||
"Something went wrong, please try again": "Something went wrong, please try again",
|
"Something went wrong, please try again": "Something went wrong, please try again",
|
||||||
|
"Song lyrics": "Song lyrics...",
|
||||||
|
"Song title": "Song title",
|
||||||
"Sorry, this address is already taken, please choose another one.": "Sorry, this address is already taken, please choose another one",
|
"Sorry, this address is already taken, please choose another one.": "Sorry, this address is already taken, please choose another one",
|
||||||
"Special projects": "Special projects",
|
"Special projects": "Special projects",
|
||||||
"Specify the source and the name of the author": "Specify the source and the name of the author",
|
"Specify the source and the name of the author": "Specify the source and the name of the author",
|
||||||
|
@ -268,6 +281,7 @@
|
||||||
"Unfollow the topic": "Unfollow the topic",
|
"Unfollow the topic": "Unfollow the topic",
|
||||||
"Unnamed draft": "Unnamed draft",
|
"Unnamed draft": "Unnamed draft",
|
||||||
"Upload": "Upload",
|
"Upload": "Upload",
|
||||||
|
"Upload error": "Upload error",
|
||||||
"Upload video": "Upload video",
|
"Upload video": "Upload video",
|
||||||
"Username": "Username",
|
"Username": "Username",
|
||||||
"Userpic": "Userpic",
|
"Userpic": "Userpic",
|
||||||
|
@ -290,6 +304,7 @@
|
||||||
"Write message": "Write a message",
|
"Write message": "Write a message",
|
||||||
"Write to us": "Write to us",
|
"Write to us": "Write to us",
|
||||||
"You are subscribed": "You are subscribed",
|
"You are subscribed": "You are subscribed",
|
||||||
|
"You can download multiple tracks at once in .mp3, .wav or .flac formats": "You can download multiple tracks at once in .mp3, .wav or .flac formats",
|
||||||
"You were successfully authorized": "You were successfully authorized",
|
"You were successfully authorized": "You were successfully authorized",
|
||||||
"You've confirmed email": "You've confirmed email",
|
"You've confirmed email": "You've confirmed email",
|
||||||
"You've reached a non-existed page": "You've reached a non-existed page",
|
"You've reached a non-existed page": "You've reached a non-existed page",
|
||||||
|
@ -322,8 +337,10 @@
|
||||||
"images": "images",
|
"images": "images",
|
||||||
"invalid password": "invalid password",
|
"invalid password": "invalid password",
|
||||||
"italic": "italic",
|
"italic": "italic",
|
||||||
|
"jpg, .png, max. 10 mb.": "jpg, .png, макс. 10 мб.",
|
||||||
"literature": "literature",
|
"literature": "literature",
|
||||||
"marker list": "marker list",
|
"marker list": "marker list",
|
||||||
|
"min. 1400×1400 pix": "мин. 1400×1400 пикс.",
|
||||||
"music": "music",
|
"music": "music",
|
||||||
"my feed": "my ribbon",
|
"my feed": "my ribbon",
|
||||||
"number list": "number list",
|
"number list": "number list",
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
"About myself": "О себе",
|
"About myself": "О себе",
|
||||||
"About the project": "О проекте",
|
"About the project": "О проекте",
|
||||||
"Accomplices": "Соучастники",
|
"Accomplices": "Соучастники",
|
||||||
"Accomplices": "Соучастники",
|
|
||||||
"Add another image": "Добавить другое изображение",
|
"Add another image": "Добавить другое изображение",
|
||||||
|
"Add audio": "Добавить аудио",
|
||||||
"Add comment": "Комментировать",
|
"Add comment": "Комментировать",
|
||||||
|
"Add cover": "Добавить обложку",
|
||||||
"Add image": "Добавить изображение",
|
"Add image": "Добавить изображение",
|
||||||
"Add images": "Добавить изображения",
|
"Add images": "Добавить изображения",
|
||||||
"Add link": "Добавить ссылку",
|
"Add link": "Добавить ссылку",
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
"Add to bookmarks": "Добавить в закладки",
|
"Add to bookmarks": "Добавить в закладки",
|
||||||
"Add url": "Добавить ссылку",
|
"Add url": "Добавить ссылку",
|
||||||
"Address on Discourse": "Адрес на Дискурсе",
|
"Address on Discourse": "Адрес на Дискурсе",
|
||||||
|
"Album name": "Название альбома",
|
||||||
"Alignment center": "По центру",
|
"Alignment center": "По центру",
|
||||||
"Alignment left": "По левому краю",
|
"Alignment left": "По левому краю",
|
||||||
"Alignment right": "По правому краю",
|
"Alignment right": "По правому краю",
|
||||||
|
@ -22,6 +24,8 @@
|
||||||
"All posts": "Все публикации",
|
"All posts": "Все публикации",
|
||||||
"All topics": "Все темы",
|
"All topics": "Все темы",
|
||||||
"Almost done! Check your email.": "Почти готово! Осталось подтвердить вашу почту.",
|
"Almost done! Check your email.": "Почти готово! Осталось подтвердить вашу почту.",
|
||||||
|
"Artist": "Исполнитель",
|
||||||
|
"Artist...": "Исполнитель...",
|
||||||
"Artworks": "Артворки",
|
"Artworks": "Артворки",
|
||||||
"Audio": "Аудио",
|
"Audio": "Аудио",
|
||||||
"Author": "Автор",
|
"Author": "Автор",
|
||||||
|
@ -66,9 +70,12 @@
|
||||||
"Create account from follow": "Создайте аккаунт, чтобы подписаться",
|
"Create account from follow": "Создайте аккаунт, чтобы подписаться",
|
||||||
"Create account from subscribe": "Создайте аккаунт для подписки на новые публикации",
|
"Create account from subscribe": "Создайте аккаунт для подписки на новые публикации",
|
||||||
"Create account from vote": "Создайте аккаунт, чтобы голосовать",
|
"Create account from vote": "Создайте аккаунт, чтобы голосовать",
|
||||||
|
"Create gallery": "Создать галерею",
|
||||||
"Create post": "Создать публикацию",
|
"Create post": "Создать публикацию",
|
||||||
|
"Create video": "Создать видео",
|
||||||
"Date of Birth": "Дата рождения",
|
"Date of Birth": "Дата рождения",
|
||||||
"Delete": "Удалить",
|
"Delete": "Удалить",
|
||||||
|
"Description": "Описание...",
|
||||||
"Discours": "Дискурс",
|
"Discours": "Дискурс",
|
||||||
"Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Дискурс — это интеллектуальная среда, веб-пространство и инструменты, которые позволяют авторам сотрудничать с читателями и объединяться для совместного создания публикаций и медиапроектов",
|
"Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Дискурс — это интеллектуальная среда, веб-пространство и инструменты, которые позволяют авторам сотрудничать с читателями и объединяться для совместного создания публикаций и медиапроектов",
|
||||||
"Discours is created with our common effort": "Дискурс существует благодаря нашему общему вкладу",
|
"Discours is created with our common effort": "Дискурс существует благодаря нашему общему вкладу",
|
||||||
|
@ -106,6 +113,8 @@
|
||||||
"Forgot password?": "Забыли пароль?",
|
"Forgot password?": "Забыли пароль?",
|
||||||
"Forward": "Переслать",
|
"Forward": "Переслать",
|
||||||
"Full name": "Имя и фамилия",
|
"Full name": "Имя и фамилия",
|
||||||
|
"Gallery name": "Название галереи",
|
||||||
|
"Genre...": "Жанр...",
|
||||||
"Get notifications": "Получать уведомления",
|
"Get notifications": "Получать уведомления",
|
||||||
"Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine": "Познакомитесь с выдающимися людьми нашего времени, участвуйте в редактировании и обсуждении статей, выступайте экспертом, оценивайте материалы других авторов со всего мира и определяйте, какие статьи будут опубликованы в журнале",
|
"Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine": "Познакомитесь с выдающимися людьми нашего времени, участвуйте в редактировании и обсуждении статей, выступайте экспертом, оценивайте материалы других авторов со всего мира и определяйте, какие статьи будут опубликованы в журнале",
|
||||||
"Go to main page": "Перейти на главную",
|
"Go to main page": "Перейти на главную",
|
||||||
|
@ -171,6 +180,7 @@
|
||||||
"My feed": "Новое",
|
"My feed": "Новое",
|
||||||
"My subscriptions": "Подписки",
|
"My subscriptions": "Подписки",
|
||||||
"Name": "Имя",
|
"Name": "Имя",
|
||||||
|
"New literary work": "Новое произведение",
|
||||||
"New only": "Только новые",
|
"New only": "Только новые",
|
||||||
"New password": "Новый пароль",
|
"New password": "Новый пароль",
|
||||||
"New stories every day and even more!": "Каждый день вас ждут новые истории и ещё много всего интересного!",
|
"New stories every day and even more!": "Каждый день вас ждут новые истории и ещё много всего интересного!",
|
||||||
|
@ -212,6 +222,7 @@
|
||||||
"Publication settings": "Настройки публикации",
|
"Publication settings": "Настройки публикации",
|
||||||
"Publications": "Публикации",
|
"Publications": "Публикации",
|
||||||
"Publish": "Опубликовать",
|
"Publish": "Опубликовать",
|
||||||
|
"Publish Album": "Опубликовать альбом",
|
||||||
"Publish Settings": "Настройки публикации",
|
"Publish Settings": "Настройки публикации",
|
||||||
"Punchline": "Панчлайн",
|
"Punchline": "Панчлайн",
|
||||||
"Quit": "Выйти",
|
"Quit": "Выйти",
|
||||||
|
@ -219,6 +230,7 @@
|
||||||
"Quotes": "Цитаты",
|
"Quotes": "Цитаты",
|
||||||
"Reason uknown": "Причина неизвестна",
|
"Reason uknown": "Причина неизвестна",
|
||||||
"Recent": "Свежее",
|
"Recent": "Свежее",
|
||||||
|
"Release date...": "Дата выхода...",
|
||||||
"Reply": "Ответить",
|
"Reply": "Ответить",
|
||||||
"Report": "Пожаловаться",
|
"Report": "Пожаловаться",
|
||||||
"Required": "Поле обязательно для заполнения",
|
"Required": "Поле обязательно для заполнения",
|
||||||
|
@ -240,9 +252,12 @@
|
||||||
"Share": "Поделиться",
|
"Share": "Поделиться",
|
||||||
"Short opening": "Небольшое вступление, чтобы заинтересовать читателя",
|
"Short opening": "Небольшое вступление, чтобы заинтересовать читателя",
|
||||||
"Show": "Показать",
|
"Show": "Показать",
|
||||||
|
"Show lyrics": "Текст песни",
|
||||||
"Social networks": "Социальные сети",
|
"Social networks": "Социальные сети",
|
||||||
"Something went wrong, check email and password": "Что-то пошло не так. Проверьте адрес электронной почты и пароль",
|
"Something went wrong, check email and password": "Что-то пошло не так. Проверьте адрес электронной почты и пароль",
|
||||||
"Something went wrong, please try again": "Что-то пошло не так, попробуйте еще раз",
|
"Something went wrong, please try again": "Что-то пошло не так, попробуйте еще раз",
|
||||||
|
"Song lyrics": "Текст песни...",
|
||||||
|
"Song title": "Название песни",
|
||||||
"Sorry, this address is already taken, please choose another one.": "Увы, этот адрес уже занят, выберите другой",
|
"Sorry, this address is already taken, please choose another one.": "Увы, этот адрес уже занят, выберите другой",
|
||||||
"Special projects": "Спецпроекты",
|
"Special projects": "Спецпроекты",
|
||||||
"Specify the source and the name of the author": "Укажите источник и имя автора",
|
"Specify the source and the name of the author": "Укажите источник и имя автора",
|
||||||
|
@ -283,6 +298,7 @@
|
||||||
"Unfollow the topic": "Отписаться от темы",
|
"Unfollow the topic": "Отписаться от темы",
|
||||||
"Unnamed draft": "Unnamed draft",
|
"Unnamed draft": "Unnamed draft",
|
||||||
"Upload": "Загрузить",
|
"Upload": "Загрузить",
|
||||||
|
"Upload error": "Ошибка загрузки",
|
||||||
"Upload video": "Загрузить видео",
|
"Upload video": "Загрузить видео",
|
||||||
"Username": "Имя пользователя",
|
"Username": "Имя пользователя",
|
||||||
"Userpic": "Аватар",
|
"Userpic": "Аватар",
|
||||||
|
@ -306,6 +322,7 @@
|
||||||
"Write message": "Написать сообщение",
|
"Write message": "Написать сообщение",
|
||||||
"Write to us": "Напишите нам",
|
"Write to us": "Напишите нам",
|
||||||
"You are subscribed": "Вы подписаны",
|
"You are subscribed": "Вы подписаны",
|
||||||
|
"You can download multiple tracks at once in .mp3, .wav or .flac formats": "Можно загрузить сразу несколько треков в форматах .mp3, .wav или .flac",
|
||||||
"You was successfully authorized": "Вы были успешно авторизованы",
|
"You was successfully authorized": "Вы были успешно авторизованы",
|
||||||
"You've confirmed email": "Вы подтвердили почту",
|
"You've confirmed email": "Вы подтвердили почту",
|
||||||
"You've reached a non-existed page": "Вы попали на несуществующую страницу",
|
"You've reached a non-existed page": "Вы попали на несуществующую страницу",
|
||||||
|
@ -341,8 +358,10 @@
|
||||||
"images": "изображения",
|
"images": "изображения",
|
||||||
"invalid password": "некорректный пароль",
|
"invalid password": "некорректный пароль",
|
||||||
"italic": "курсив",
|
"italic": "курсив",
|
||||||
|
"jpg, .png, max. 10 mb.": "jpg, .png, макс. 10 мб.",
|
||||||
"literature": "литература",
|
"literature": "литература",
|
||||||
"marker list": "маркир. список",
|
"marker list": "маркир. список",
|
||||||
|
"min. 1400×1400 pix": "мин. 1400×1400 пикс.",
|
||||||
"music": "музыка",
|
"music": "музыка",
|
||||||
"my feed": "моя лента",
|
"my feed": "моя лента",
|
||||||
"number list": "нумер. список",
|
"number list": "нумер. список",
|
||||||
|
|
|
@ -291,8 +291,9 @@ img {
|
||||||
}
|
}
|
||||||
|
|
||||||
.shoutStatsItem {
|
.shoutStatsItem {
|
||||||
align-items: center;
|
|
||||||
@include font-size(1.5rem);
|
@include font-size(1.5rem);
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 0 6% 1em 0;
|
margin: 0 6% 1em 0;
|
||||||
|
|
79
src/components/Article/AudioHeader/AudioHeader.module.scss
Normal file
79
src/components/Article/AudioHeader/AudioHeader.module.scss
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
.AudioHeader {
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
|
||||||
|
.albumInfo {
|
||||||
|
margin-right: 224px;
|
||||||
|
|
||||||
|
.topic {
|
||||||
|
.link {
|
||||||
|
@include font-size(1.6rem);
|
||||||
|
|
||||||
|
color: var(--blue-link);
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > h1 {
|
||||||
|
margin: 16px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artistData {
|
||||||
|
margin: 18px 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
@include font-size(1.6rem);
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 2px 12px;
|
||||||
|
border-left: 2px solid #e9e9ee;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-left: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
float: right;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
background: var(--placeholder-color-semi) url('icons/create-music.svg') no-repeat 50% 50%;
|
||||||
|
|
||||||
|
.image {
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.expandedImage {
|
||||||
|
.cover {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.albumInfo {
|
||||||
|
margin-right: 0;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
src/components/Article/AudioHeader/AudioHeader.tsx
Normal file
53
src/components/Article/AudioHeader/AudioHeader.tsx
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './AudioHeader.module.scss'
|
||||||
|
import { imageProxy } from '../../../utils/imageProxy'
|
||||||
|
import { MediaItem } from '../../../pages/types'
|
||||||
|
import { createSignal, Show } from 'solid-js'
|
||||||
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
import { Topic } from '../../../graphql/types.gen'
|
||||||
|
import { getPagePath } from '@nanostores/router'
|
||||||
|
import { router } from '../../../stores/router'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
title: string
|
||||||
|
cover?: string
|
||||||
|
artistData?: MediaItem
|
||||||
|
topic: Topic
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AudioHeader = (props: Props) => {
|
||||||
|
const [expandedImage, setExpandedImage] = createSignal(false)
|
||||||
|
return (
|
||||||
|
<div class={clsx(styles.AudioHeader, { [styles.expandedImage]: expandedImage() })}>
|
||||||
|
<div class={styles.cover}>
|
||||||
|
<img class={styles.image} src={imageProxy(props.cover)} alt={props.title} />
|
||||||
|
<Show when={props.cover}>
|
||||||
|
<button type="button" class={styles.expand} onClick={() => setExpandedImage(!expandedImage())}>
|
||||||
|
<Icon name="expand-circle" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<div class={styles.albumInfo}>
|
||||||
|
<Show when={props.topic}>
|
||||||
|
<div class={styles.topic}>
|
||||||
|
<a href={getPagePath(router, 'topic', { slug: props.topic.slug })} class={styles.link}>
|
||||||
|
{props.topic.title}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<h1>{props.title}</h1>
|
||||||
|
<div class={styles.artistData}>
|
||||||
|
<Show when={props.artistData.artist}>
|
||||||
|
<div class={styles.item}>{props.artistData.artist}</div>
|
||||||
|
</Show>
|
||||||
|
<Show when={props.artistData.date}>
|
||||||
|
<div class={styles.item}>{props.artistData.date}</div>
|
||||||
|
</Show>
|
||||||
|
<Show when={props.artistData.genre}>
|
||||||
|
<div class={styles.item}>{props.artistData.genre}</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
src/components/Article/AudioHeader/index.ts
Normal file
1
src/components/Article/AudioHeader/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { AudioHeader } from './AudioHeader'
|
|
@ -46,20 +46,20 @@
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.playButton {
|
.playButton {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
background-color: #141414;
|
background: #141414;
|
||||||
|
|
||||||
& img {
|
& img {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,22 +113,25 @@
|
||||||
|
|
||||||
.progressFilled {
|
.progressFilled {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
left: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-bottom: 4px solid #141414;
|
border-bottom: 4px solid var(--default-color);
|
||||||
|
transition: width 0.3s linear;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -8px;
|
bottom: -10px;
|
||||||
right: -8px;
|
right: -8px;
|
||||||
|
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 4px solid #141414;
|
border: 4px solid var(--default-color);
|
||||||
background-color: #fff;
|
background-color: var(--background-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +230,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 32px 0 58px;
|
margin: 32px 0 16px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
& > li {
|
& > li {
|
||||||
|
@ -243,6 +246,11 @@
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.playlistItemPlayButton {
|
.playlistItemPlayButton {
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -250,18 +258,42 @@
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlistItemTitle {
|
.playlistItemText {
|
||||||
max-width: 254px;
|
@include font-size(1.6rem);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex: 1;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
margin: 0 16px;
|
||||||
|
gap: 16px;
|
||||||
|
color: var(--default-color);
|
||||||
|
|
||||||
margin-left: 17px;
|
.artist,
|
||||||
font-weight: 400;
|
.title {
|
||||||
font-size: 16px;
|
@include font-size(1.6rem);
|
||||||
line-height: 22px;
|
|
||||||
letter-spacing: -0.01em;
|
overflow: hidden;
|
||||||
color: #000000;
|
max-width: calc(50% - 16px);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlistItemControls {
|
.playlistItemControls {
|
||||||
|
@ -275,7 +307,7 @@
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
color: #000000;
|
color: var(--default-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.timelinePlaceholder {
|
.timelinePlaceholder {
|
||||||
|
@ -293,6 +325,29 @@
|
||||||
height: 67px;
|
height: 67px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shareMedia {
|
.actions {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.descriptionBlock {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 8px 0 24px 0;
|
||||||
|
|
||||||
|
.description,
|
||||||
|
.lyrics {
|
||||||
|
@include font-size(1.4rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
& > textarea::placeholder {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +1,63 @@
|
||||||
import { createEffect, createSignal, onMount, Show } from 'solid-js'
|
import { createEffect, createSignal, on, onMount, Show } from 'solid-js'
|
||||||
|
|
||||||
import { PlayerHeader } from './PlayerHeader'
|
import { PlayerHeader } from './PlayerHeader'
|
||||||
import { PlayerPlaylist } from './PlayerPlaylist'
|
import { PlayerPlaylist } from './PlayerPlaylist'
|
||||||
|
|
||||||
import styles from './AudioPlayer.module.scss'
|
import styles from './AudioPlayer.module.scss'
|
||||||
|
import { MediaItem } from '../../../pages/types'
|
||||||
|
|
||||||
export type MediaItem = {
|
export type Audio = {
|
||||||
id?: number
|
pic?: string
|
||||||
body: string
|
index?: number
|
||||||
pic: string
|
isCurrent?: boolean
|
||||||
title: string
|
isPlaying?: boolean
|
||||||
url: string
|
} & MediaItem
|
||||||
isCurrent: boolean
|
|
||||||
isPlaying: boolean
|
type Props = {
|
||||||
|
media: Audio[]
|
||||||
|
articleSlug?: string
|
||||||
|
body?: string
|
||||||
|
editorMode?: boolean
|
||||||
|
onAudioChange?: (index: number, field: string, value: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const prepareMedia = (media: MediaItem[]) =>
|
const prepareMedia = (media: Audio[]) =>
|
||||||
media.map((item, index) => ({
|
media.map((item, index) => ({
|
||||||
...item,
|
...item,
|
||||||
id: index,
|
index: index,
|
||||||
isCurrent: false,
|
isCurrent: false,
|
||||||
isPlaying: false
|
isPlaying: false
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const progressUpdate = (audioRef, progressFilledRef, duration) => {
|
const progressUpdate = (audioRef, progressFilledRef, duration) => {
|
||||||
progressFilledRef.style.width = `${(audioRef.currentTime / duration) * 100 || 0}%`
|
progressFilledRef.current.style.width = `${(audioRef.current.currentTime / duration) * 100 || 0}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
const scrub = (event, progressRef, duration, audioRef) => {
|
const scrub = (event, progressRef, duration, audioRef) => {
|
||||||
audioRef.currentTime = (event.offsetX / progressRef.offsetWidth) * duration
|
audioRef.current.currentTime = (event.offsetX / progressRef.current.offsetWidth) * duration
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFormattedTime = (point) => new Date(point * 1000).toISOString().slice(14, -5)
|
const getFormattedTime = (point) => new Date(point * 1000).toISOString().slice(14, -5)
|
||||||
|
|
||||||
export default (props: { media: MediaItem[]; articleSlug: string; body: string }) => {
|
export const AudioPlayer = (props: Props) => {
|
||||||
let audioRef: HTMLAudioElement
|
const audioRef: { current: HTMLAudioElement } = { current: null }
|
||||||
let progressRef: HTMLDivElement
|
const progressRef: { current: HTMLDivElement } = { current: null }
|
||||||
let progressFilledRef: HTMLDivElement
|
const progressFilledRef: { current: HTMLDivElement } = { current: null }
|
||||||
|
|
||||||
const [audioContext, setAudioContext] = createSignal<AudioContext>()
|
const [audioContext, setAudioContext] = createSignal<AudioContext>()
|
||||||
const [gainNode, setGainNode] = createSignal<GainNode>()
|
const [gainNode, setGainNode] = createSignal<GainNode>()
|
||||||
|
const [tracks, setTracks] = createSignal<Audio[] | null>(prepareMedia(props.media))
|
||||||
const [tracks, setTracks] = createSignal<MediaItem[] | null>(prepareMedia(props.media))
|
|
||||||
|
|
||||||
const [duration, setDuration] = createSignal<number>(0)
|
const [duration, setDuration] = createSignal<number>(0)
|
||||||
const [currentTimeContent, setCurrentTimeContent] = createSignal<string>('00:00')
|
const [currentTimeContent, setCurrentTimeContent] = createSignal<string>('00:00')
|
||||||
const [currentDurationContent, setCurrentDurationContent] = createSignal<string>('00:00')
|
const [currentDurationContent, setCurrentDurationContent] = createSignal<string>('00:00')
|
||||||
|
|
||||||
const [mousedown, setMousedown] = createSignal<boolean>(false)
|
const [mousedown, setMousedown] = createSignal<boolean>(false)
|
||||||
|
|
||||||
|
createEffect(
|
||||||
|
on(
|
||||||
|
() => props.media,
|
||||||
|
() => {
|
||||||
|
setTracks(prepareMedia(props.media))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
const getCurrentTrack = () =>
|
const getCurrentTrack = () =>
|
||||||
tracks().find((track) => track.isCurrent) ||
|
tracks().find((track) => track.isCurrent) ||
|
||||||
(() => {
|
(() => {
|
||||||
|
@ -63,10 +72,10 @@ export default (props: { media: MediaItem[]; articleSlug: string; body: string }
|
||||||
})()
|
})()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (audioRef.src !== getCurrentTrack().url) {
|
if (audioRef.current.src !== getCurrentTrack().url) {
|
||||||
audioRef.src = getCurrentTrack().url
|
audioRef.current.src = getCurrentTrack().url
|
||||||
|
|
||||||
audioRef.load()
|
audioRef.current.load()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -76,33 +85,33 @@ export default (props: { media: MediaItem[]; articleSlug: string; body: string }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const playMedia = async (m: MediaItem) => {
|
const playMedia = async (m: Audio) => {
|
||||||
setTracks(
|
setTracks(
|
||||||
tracks().map((track) => ({
|
tracks().map((track) => ({
|
||||||
...track,
|
...track,
|
||||||
isCurrent: track.id === m.id,
|
isCurrent: track.index === m.index,
|
||||||
isPlaying: track.id === m.id ? !track.isPlaying : false
|
isPlaying: track.index === m.index ? !track.isPlaying : false
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
progressUpdate(audioRef, progressFilledRef, duration())
|
progressUpdate(audioRef, progressFilledRef, duration())
|
||||||
|
|
||||||
if (audioContext().state === 'suspended') audioContext().resume()
|
if (audioContext().state === 'suspended') await audioContext().resume()
|
||||||
|
|
||||||
if (getCurrentTrack().isPlaying) {
|
if (getCurrentTrack().isPlaying) {
|
||||||
await audioRef.play()
|
await audioRef.current.play()
|
||||||
} else {
|
} else {
|
||||||
audioRef.pause()
|
audioRef.current.pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setTimes = () => {
|
const setTimes = () => {
|
||||||
setCurrentTimeContent(getFormattedTime(audioRef.currentTime))
|
setCurrentTimeContent(getFormattedTime(audioRef.current.currentTime))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAudioEnd = () => {
|
const handleAudioEnd = () => {
|
||||||
progressFilledRef.style.width = '0%'
|
progressFilledRef.current.style.width = '0%'
|
||||||
audioRef.currentTime = 0
|
audioRef.current.currentTime = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAudioTimeUpdate = () => {
|
const handleAudioTimeUpdate = () => {
|
||||||
|
@ -117,42 +126,42 @@ export default (props: { media: MediaItem[]; articleSlug: string; body: string }
|
||||||
|
|
||||||
setTimes()
|
setTimes()
|
||||||
|
|
||||||
const track = audioContext().createMediaElementSource(audioRef)
|
const track = audioContext().createMediaElementSource(audioRef.current)
|
||||||
track.connect(gainNode()).connect(audioContext().destination)
|
track.connect(gainNode()).connect(audioContext().destination)
|
||||||
})
|
})
|
||||||
|
|
||||||
const playPrevTrack = () => {
|
const playPrevTrack = () => {
|
||||||
const { id } = getCurrentTrack()
|
const { index } = getCurrentTrack()
|
||||||
const currIndex = tracks().findIndex((track) => track.id === id)
|
const currIndex = tracks().findIndex((track) => track.index === index)
|
||||||
|
|
||||||
const getUpdatedStatus = (trackId) =>
|
const getUpdatedStatus = (trackId) =>
|
||||||
currIndex === 0
|
currIndex === 0
|
||||||
? trackId === tracks()[tracks().length - 1].id
|
? trackId === tracks()[tracks().length - 1].index
|
||||||
: trackId === tracks()[currIndex - 1].id
|
: trackId === tracks()[currIndex - 1].index
|
||||||
|
|
||||||
setTracks(
|
setTracks(
|
||||||
tracks().map((track) => ({
|
tracks().map((track) => ({
|
||||||
...track,
|
...track,
|
||||||
isCurrent: getUpdatedStatus(track.id),
|
isCurrent: getUpdatedStatus(track.index),
|
||||||
isPlaying: getUpdatedStatus(track.id)
|
isPlaying: getUpdatedStatus(track.index)
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const playNextTrack = () => {
|
const playNextTrack = () => {
|
||||||
const { id } = getCurrentTrack()
|
const { index } = getCurrentTrack()
|
||||||
const currIndex = tracks().findIndex((track) => track.id === id)
|
const currIndex = tracks().findIndex((track) => track.index === index)
|
||||||
|
|
||||||
const getUpdatedStatus = (trackId) =>
|
const getUpdatedStatus = (trackId) =>
|
||||||
currIndex === tracks().length - 1
|
currIndex === tracks().length - 1
|
||||||
? trackId === tracks()[0].id
|
? trackId === tracks()[0].index
|
||||||
: trackId === tracks()[currIndex + 1].id
|
: trackId === tracks()[currIndex + 1].index
|
||||||
|
|
||||||
setTracks(
|
setTracks(
|
||||||
tracks().map((track) => ({
|
tracks().map((track) => ({
|
||||||
...track,
|
...track,
|
||||||
isCurrent: getUpdatedStatus(track.id),
|
isCurrent: getUpdatedStatus(track.index),
|
||||||
isPlaying: getUpdatedStatus(track.id)
|
isPlaying: getUpdatedStatus(track.index)
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -161,6 +170,15 @@ export default (props: { media: MediaItem[]; articleSlug: string; body: string }
|
||||||
setDuration(target.duration)
|
setDuration(target.duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleAudioDescriptionChange = (index: number, field: string, value) => {
|
||||||
|
props.onAudioChange(index, field, value)
|
||||||
|
setTracks(
|
||||||
|
tracks().map((track, idx) => {
|
||||||
|
return idx === index ? { ...track, [field]: value } : track
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Show when={getCurrentTrack()}>
|
<Show when={getCurrentTrack()}>
|
||||||
|
@ -177,24 +195,24 @@ export default (props: { media: MediaItem[]; articleSlug: string; body: string }
|
||||||
<div class={styles.timeline}>
|
<div class={styles.timeline}>
|
||||||
<div
|
<div
|
||||||
class={styles.progress}
|
class={styles.progress}
|
||||||
ref={progressRef}
|
ref={(el) => (progressRef.current = el)}
|
||||||
onClick={(e) => scrub(e, progressRef, duration(), audioRef)}
|
onClick={(e) => scrub(e, progressRef, duration(), audioRef)}
|
||||||
onMouseMove={(e) => mousedown() && scrub(e, progressRef, duration(), audioRef)}
|
onMouseMove={(e) => mousedown() && scrub(e, progressRef, duration(), audioRef)}
|
||||||
onMouseDown={() => setMousedown(true)}
|
onMouseDown={() => setMousedown(true)}
|
||||||
onMouseUp={() => setMousedown(false)}
|
onMouseUp={() => setMousedown(false)}
|
||||||
>
|
>
|
||||||
<div class={styles.progressFilled} ref={progressFilledRef}></div>
|
<div class={styles.progressFilled} ref={(el) => (progressFilledRef.current = el)} />
|
||||||
</div>
|
</div>
|
||||||
<div class={styles.progressTiming}>
|
<div class={styles.progressTiming}>
|
||||||
<span>{currentTimeContent()}</span>
|
<span>{currentTimeContent()}</span>
|
||||||
<span>{currentDurationContent()}</span>
|
<span>{currentDurationContent()}</span>
|
||||||
</div>
|
</div>
|
||||||
<audio
|
<audio
|
||||||
ref={audioRef}
|
ref={(el) => (audioRef.current = el)}
|
||||||
onTimeUpdate={handleAudioTimeUpdate}
|
onTimeUpdate={handleAudioTimeUpdate}
|
||||||
onCanPlay={() => {
|
onCanPlay={() => {
|
||||||
if (getCurrentTrack().isPlaying) {
|
if (getCurrentTrack().isPlaying) {
|
||||||
audioRef.play()
|
audioRef.current.play()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onLoadedMetadata={handleOnAudioMetadataLoad}
|
onLoadedMetadata={handleOnAudioMetadataLoad}
|
||||||
|
@ -203,14 +221,15 @@ export default (props: { media: MediaItem[]; articleSlug: string; body: string }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={tracks()}>
|
<Show when={tracks()}>
|
||||||
<PlayerPlaylist
|
<PlayerPlaylist
|
||||||
|
editorMode={props.editorMode}
|
||||||
playMedia={playMedia}
|
playMedia={playMedia}
|
||||||
tracks={tracks()}
|
tracks={tracks()}
|
||||||
getCurrentTrack={getCurrentTrack}
|
currentTrack={getCurrentTrack()}
|
||||||
articleSlug={props.articleSlug}
|
articleSlug={props.articleSlug}
|
||||||
body={props.body}
|
body={props.body}
|
||||||
|
onAudioChange={handleAudioDescriptionChange}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -35,29 +35,29 @@ export const PlayerHeader = (props) => {
|
||||||
<div class={styles.playerTitle}>{getCurrentTrack().title}</div>
|
<div class={styles.playerTitle}>{getCurrentTrack().title}</div>
|
||||||
<div class={styles.playerControls}>
|
<div class={styles.playerControls}>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={onPlayMedia}
|
onClick={onPlayMedia}
|
||||||
class={clsx(
|
class={clsx(
|
||||||
styles.playButton,
|
styles.playButton,
|
||||||
getCurrentTrack().isPlaying ? styles.playButtonInvertPause : styles.playButtonInvertPlay
|
getCurrentTrack().isPlaying ? styles.playButtonInvertPause : styles.playButtonInvertPlay
|
||||||
)}
|
)}
|
||||||
role="button"
|
|
||||||
aria-label="Play"
|
aria-label="Play"
|
||||||
data-playing="false"
|
data-playing="false"
|
||||||
>
|
>
|
||||||
<Icon name={getCurrentTrack().isPlaying ? 'pause' : 'play'} />
|
<Icon name={getCurrentTrack().isPlaying ? 'pause' : 'play'} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={playPrevTrack}
|
onClick={playPrevTrack}
|
||||||
class={clsx(styles.controlsButton)}
|
class={clsx(styles.controlsButton)}
|
||||||
role="button"
|
|
||||||
aria-label="Previous"
|
aria-label="Previous"
|
||||||
>
|
>
|
||||||
<Icon name="player-arrow" />
|
<Icon name="player-arrow" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={playNextTrack}
|
onClick={playNextTrack}
|
||||||
class={clsx(styles.controlsButton, styles.controlsButtonNext)}
|
class={clsx(styles.controlsButton, styles.controlsButtonNext)}
|
||||||
role="button"
|
|
||||||
aria-label="Next"
|
aria-label="Next"
|
||||||
>
|
>
|
||||||
<Icon name="player-arrow" />
|
<Icon name="player-arrow" />
|
||||||
|
|
|
@ -1,61 +1,159 @@
|
||||||
import { For } from 'solid-js'
|
import { createEffect, createSignal, For, Show } from 'solid-js'
|
||||||
|
|
||||||
import { SharePopup, getShareUrl } from '../SharePopup'
|
import { SharePopup, getShareUrl } from '../SharePopup'
|
||||||
import { getDescription } from '../../../utils/meta'
|
import { getDescription } from '../../../utils/meta'
|
||||||
|
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
import type { Audio } from './AudioPlayer'
|
||||||
import type { MediaItem } from './AudioPlayer'
|
|
||||||
|
|
||||||
import { Popover } from '../../_shared/Popover'
|
import { Popover } from '../../_shared/Popover'
|
||||||
import { Icon } from '../../_shared/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
|
||||||
import styles from './AudioPlayer.module.scss'
|
import styles from './AudioPlayer.module.scss'
|
||||||
|
import { GrowingTextarea } from '../../_shared/GrowingTextarea'
|
||||||
|
import MD from '../MD'
|
||||||
|
|
||||||
export const PlayerPlaylist = (props) => {
|
type Props = {
|
||||||
|
tracks: Audio[]
|
||||||
|
currentTrack: Audio
|
||||||
|
playMedia: (audio: Audio) => void
|
||||||
|
articleSlug?: string
|
||||||
|
body?: string
|
||||||
|
editorMode?: boolean
|
||||||
|
onAudioChange?: (index: number, field: string, value: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PlayerPlaylist = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
|
const [activeEditIndex, setActiveEditIndex] = createSignal(-1)
|
||||||
|
|
||||||
const { tracks, getCurrentTrack, playMedia, articleSlug, body } = props
|
const toggleDropDown = (index) => {
|
||||||
|
setActiveEditIndex(activeEditIndex() === index ? -1 : index)
|
||||||
|
}
|
||||||
|
const updateData = (key, value) => {
|
||||||
|
props.onAudioChange(activeEditIndex(), key, value)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul class={styles.playlist}>
|
<ul class={styles.playlist}>
|
||||||
<For each={tracks}>
|
<For each={props.tracks}>
|
||||||
{(m: MediaItem) => (
|
{(m: Audio, index) => (
|
||||||
<li class={styles.playlistItem}>
|
<li>
|
||||||
<button
|
<div class={styles.playlistItem}>
|
||||||
class={styles.playlistItemPlayButton}
|
<button
|
||||||
onClick={() => playMedia(m)}
|
class={styles.playlistItemPlayButton}
|
||||||
role="button"
|
onClick={() => props.playMedia(m)}
|
||||||
aria-label="Play"
|
type="button"
|
||||||
>
|
aria-label="Play"
|
||||||
<Icon
|
>
|
||||||
name={
|
<Icon
|
||||||
getCurrentTrack() && getCurrentTrack().id === m.id && getCurrentTrack().isPlaying
|
name={
|
||||||
? 'pause'
|
props.currentTrack &&
|
||||||
: 'play'
|
props.currentTrack.index === m.index &&
|
||||||
}
|
props.currentTrack.isPlaying
|
||||||
/>
|
? 'pause'
|
||||||
</button>
|
: 'play'
|
||||||
<div class={styles.playlistItemTitle}>{m.title}</div>
|
}
|
||||||
<div class={styles.shareMedia}>
|
/>
|
||||||
<Popover content={t('Share')}>
|
</button>
|
||||||
{(triggerRef: (el) => void) => (
|
<div class={styles.playlistItemText}>
|
||||||
<div ref={triggerRef}>
|
<Show
|
||||||
<SharePopup
|
when={activeEditIndex() === index() && props.editorMode}
|
||||||
title={m.title}
|
fallback={
|
||||||
description={getDescription(body)}
|
<>
|
||||||
imageUrl={m.pic}
|
<div class={styles.title}>
|
||||||
shareUrl={getShareUrl({ pathname: `/${articleSlug}` })}
|
{m.title.replace(/\.(wav|flac|mp3|aac)$/i, '') || t('Song title')}
|
||||||
trigger={
|
</div>
|
||||||
<div>
|
<div class={styles.artist}>{m.artist || t('Artist')}</div>
|
||||||
<Icon name="share-media" />
|
</>
|
||||||
</div>
|
}
|
||||||
}
|
>
|
||||||
/>
|
<input
|
||||||
</div>
|
type="text"
|
||||||
)}
|
value={m.title}
|
||||||
</Popover>
|
class={styles.title}
|
||||||
|
placeholder={t('Song title')}
|
||||||
|
onChange={(e) => updateData('title', e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={m.artist}
|
||||||
|
class={styles.artist}
|
||||||
|
placeholder={t('Artist')}
|
||||||
|
onChange={(e) => updateData('artist', e.target.value)}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<div class={styles.actions}>
|
||||||
|
<Show when={(m.lyrics || m.body) && !props.editorMode}>
|
||||||
|
<Popover content={t('Show lyrics')}>
|
||||||
|
{(triggerRef: (el) => void) => (
|
||||||
|
<button ref={triggerRef} type="button" onClick={() => toggleDropDown(index())}>
|
||||||
|
<Icon name="list" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Popover>
|
||||||
|
</Show>
|
||||||
|
<Popover content={props.editorMode ? t('Edit') : t('Share')}>
|
||||||
|
{(triggerRef: (el) => void) => (
|
||||||
|
<div ref={triggerRef}>
|
||||||
|
<Show
|
||||||
|
when={!props.editorMode}
|
||||||
|
fallback={
|
||||||
|
<button type="button" onClick={() => toggleDropDown(index())}>
|
||||||
|
<Icon name="pencil-stroke" />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SharePopup
|
||||||
|
title={m.title}
|
||||||
|
description={getDescription(props.body)}
|
||||||
|
imageUrl={m.pic}
|
||||||
|
shareUrl={getShareUrl({ pathname: `/${props.articleSlug}` })}
|
||||||
|
trigger={
|
||||||
|
<div>
|
||||||
|
<Icon name="share-media" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={activeEditIndex() === index()}>
|
||||||
|
<Show
|
||||||
|
when={props.editorMode}
|
||||||
|
fallback={
|
||||||
|
<div class={styles.descriptionBlock}>
|
||||||
|
<Show when={m.body}>
|
||||||
|
<div class={styles.description}>
|
||||||
|
<MD body={m.body} />
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<Show when={m.lyrics}>
|
||||||
|
<div class={styles.lyrics}>
|
||||||
|
<MD body={m.lyrics} />
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div class={styles.descriptionBlock}>
|
||||||
|
<GrowingTextarea
|
||||||
|
allowEnterKey={true}
|
||||||
|
class={styles.description}
|
||||||
|
placeholder={t('Description')}
|
||||||
|
value={(value) => updateData('body', value)}
|
||||||
|
initialValue={m.body || ''}
|
||||||
|
/>
|
||||||
|
<GrowingTextarea
|
||||||
|
allowEnterKey={true}
|
||||||
|
class={styles.lyrics}
|
||||||
|
placeholder={t('Song lyrics')}
|
||||||
|
value={(value) => updateData('lyrics', value)}
|
||||||
|
initialValue={m.lyrics || ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</Show>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
|
1
src/components/Article/AudioPlayer/index.ts
Normal file
1
src/components/Article/AudioPlayer/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { AudioPlayer } from './AudioPlayer'
|
|
@ -1,7 +1,7 @@
|
||||||
import { capitalize, formatDate } from '../../utils'
|
import { capitalize, formatDate } from '../../utils'
|
||||||
import { Icon } from '../_shared/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { AuthorCard } from '../Author/AuthorCard'
|
import { AuthorCard } from '../Author/AuthorCard'
|
||||||
import AudioPlayer from './AudioPlayer/AudioPlayer'
|
import { AudioPlayer } from './AudioPlayer'
|
||||||
import type { Author, Shout } from '../../graphql/types.gen'
|
import type { Author, Shout } from '../../graphql/types.gen'
|
||||||
import MD from './MD'
|
import MD from './MD'
|
||||||
import { SharePopup } from './SharePopup'
|
import { SharePopup } from './SharePopup'
|
||||||
|
@ -21,20 +21,15 @@ import styles from './Article.module.scss'
|
||||||
import { imageProxy } from '../../utils/imageProxy'
|
import { imageProxy } from '../../utils/imageProxy'
|
||||||
import { Popover } from '../_shared/Popover'
|
import { Popover } from '../_shared/Popover'
|
||||||
import article from '../Editor/extensions/Article'
|
import article from '../Editor/extensions/Article'
|
||||||
import { SolidSwiper } from '../_shared/SolidSwiper'
|
import { createEffect, For, createMemo, onMount, Show, createSignal, Switch, Match } from 'solid-js'
|
||||||
import { createEffect, For, createMemo, Match, onMount, Show, Switch, createSignal } from 'solid-js'
|
import { MediaItem } from '../../pages/types'
|
||||||
|
import { AudioHeader } from './AudioHeader'
|
||||||
|
|
||||||
interface ArticleProps {
|
interface ArticleProps {
|
||||||
article: Shout
|
article: Shout
|
||||||
scrollToComments?: boolean
|
scrollToComments?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MediaItem {
|
|
||||||
url?: string
|
|
||||||
title?: string
|
|
||||||
body?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FullArticle = (props: ArticleProps) => {
|
export const FullArticle = (props: ArticleProps) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const {
|
const {
|
||||||
|
@ -117,40 +112,52 @@ export const FullArticle = (props: ArticleProps) => {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<article class="col-md-16 col-lg-14 col-xl-12 offset-md-5">
|
<article class="col-md-16 col-lg-14 col-xl-12 offset-md-5">
|
||||||
{/*TODO: Check styles.shoutTopic*/}
|
{/*TODO: Check styles.shoutTopic*/}
|
||||||
<div class={styles.shoutHeader}>
|
<Switch>
|
||||||
<Show when={mainTopic()}>
|
<Match when={props.article.layout !== 'audio'}>
|
||||||
<div class={styles.shoutTopic}>
|
<div class={styles.shoutHeader}>
|
||||||
<a
|
<Show when={mainTopic()}>
|
||||||
href={getPagePath(router, 'topic', { slug: props.article.mainTopic })}
|
<div class={styles.shoutTopic}>
|
||||||
class={styles.mainTopicLink}
|
<a
|
||||||
>
|
href={getPagePath(router, 'topic', { slug: props.article.mainTopic })}
|
||||||
{mainTopic().title}
|
class={styles.mainTopicLink}
|
||||||
</a>
|
>
|
||||||
|
{mainTopic().title}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<h1>{props.article.title}</h1>
|
||||||
|
<Show when={props.article.subtitle}>
|
||||||
|
<h4>{capitalize(props.article.subtitle, false)}</h4>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<div class={styles.shoutAuthor}>
|
||||||
|
<For each={props.article.authors}>
|
||||||
|
{(a: Author, index) => (
|
||||||
|
<>
|
||||||
|
<Show when={index() > 0}>, </Show>
|
||||||
|
<a href={getPagePath(router, 'author', { slug: a.slug })}>{a.name}</a>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
<Show when={props.article.cover && props.article.layout !== 'video'}>
|
||||||
|
<div
|
||||||
|
class={styles.shoutCover}
|
||||||
|
style={{ 'background-image': `url('${imageProxy(props.article.cover)}')` }}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Match>
|
||||||
|
<Match when={props.article.layout === 'audio'}>
|
||||||
<h1>{props.article.title}</h1>
|
<AudioHeader
|
||||||
<Show when={props.article.subtitle}>
|
title={props.article.title}
|
||||||
<h4>{capitalize(props.article.subtitle, false)}</h4>
|
cover={props.article.cover}
|
||||||
</Show>
|
artistData={media()[0]}
|
||||||
|
topic={mainTopic()}
|
||||||
<div class={styles.shoutAuthor}>
|
|
||||||
<For each={props.article.authors}>
|
|
||||||
{(a: Author, index) => (
|
|
||||||
<>
|
|
||||||
<Show when={index() > 0}>, </Show>
|
|
||||||
<a href={getPagePath(router, 'author', { slug: a.slug })}>{a.name}</a>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
<Show when={props.article.cover && props.article.layout !== 'video'}>
|
|
||||||
<div
|
|
||||||
class={styles.shoutCover}
|
|
||||||
style={{ 'background-image': `url('${imageProxy(props.article.cover)}')` }}
|
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Match>
|
||||||
</div>
|
</Switch>
|
||||||
|
|
||||||
<Show when={media() && props.article.layout === 'video'}>
|
<Show when={media() && props.article.layout === 'video'}>
|
||||||
<div class="media-items">
|
<div class="media-items">
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
.AudioUploader {
|
||||||
|
display: block;
|
||||||
|
margin-top: 2rem;
|
||||||
|
|
||||||
|
.draggable {
|
||||||
|
margin: 8px 0;
|
||||||
|
padding: 8px 0;
|
||||||
|
&:hover {
|
||||||
|
background: var(--placeholder-color-semi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable {
|
||||||
|
background: red;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
41
src/components/Editor/AudioUploader/AudioUploader.tsx
Normal file
41
src/components/Editor/AudioUploader/AudioUploader.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './AudioUploader.module.scss'
|
||||||
|
import { DropArea } from '../../_shared/DropArea'
|
||||||
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
import { createEffect, createSignal, on, Show } from 'solid-js'
|
||||||
|
import { MediaItem } from '../../../pages/types'
|
||||||
|
import { composeMediaItems } from '../../../utils/composeMediaItems'
|
||||||
|
import { AudioPlayer } from '../../Article/AudioPlayer'
|
||||||
|
import { Buffer } from 'buffer'
|
||||||
|
|
||||||
|
window.Buffer = Buffer
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
class?: string
|
||||||
|
audio: MediaItem[]
|
||||||
|
onAudioChange: (index: number, value: MediaItem) => void
|
||||||
|
onAudioAdd: (value: MediaItem[]) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AudioUploader = (props: Props) => {
|
||||||
|
const { t } = useLocalize()
|
||||||
|
|
||||||
|
const handleAudioDescriptionChange = (index: number, field: string, value) => {
|
||||||
|
props.onAudioChange(index, { ...props.audio[index], [field]: value })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={clsx(styles.AudioUploader, props.class)}>
|
||||||
|
<Show when={props.audio.length > 0}>
|
||||||
|
<AudioPlayer editorMode={true} media={props.audio} onAudioChange={handleAudioDescriptionChange} />
|
||||||
|
</Show>
|
||||||
|
<DropArea
|
||||||
|
isMultiply={true}
|
||||||
|
placeholder={t('Add audio')}
|
||||||
|
description={t('You can download multiple tracks at once in .mp3, .wav or .flac formats')}
|
||||||
|
fileType={'audio'}
|
||||||
|
onUpload={(value) => props.onAudioAdd(composeMediaItems(value))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
src/components/Editor/AudioUploader/index.ts
Normal file
1
src/components/Editor/AudioUploader/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { AudioUploader } from './AudioUploader'
|
|
@ -90,6 +90,34 @@
|
||||||
.titleInput {
|
.titleInput {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
.additional {
|
||||||
|
margin-top: auto;
|
||||||
|
|
||||||
|
.additionalInput {
|
||||||
|
@include font-size(1.4rem);
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0;
|
||||||
|
margin: 14px 0 0;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datepicker {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin: 14px 0 0;
|
||||||
|
|
||||||
|
.additionalInput {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grow input
|
// Grow input
|
||||||
|
@ -215,13 +243,36 @@
|
||||||
|
|
||||||
.inputContainer {
|
.inputContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
.validationError {
|
.validationError {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
top: 100%;
|
top: calc(100% + 4px);
|
||||||
font-size: small;
|
font-size: small;
|
||||||
color: #f00;
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.audioHeader {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
.inputContainer {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
width: 228px;
|
||||||
|
height: 228px;
|
||||||
|
flex-basis: 228px;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
|
import { Accessor, createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { Title } from '@solidjs/meta'
|
import { Title } from '@solidjs/meta'
|
||||||
|
@ -16,9 +16,12 @@ import { hideModal, showModal } from '../../stores/ui'
|
||||||
import { imageProxy } from '../../utils/imageProxy'
|
import { imageProxy } from '../../utils/imageProxy'
|
||||||
import { GrowingTextarea } from '../_shared/GrowingTextarea'
|
import { GrowingTextarea } from '../_shared/GrowingTextarea'
|
||||||
import { VideoUploader } from '../Editor/VideoUploader'
|
import { VideoUploader } from '../Editor/VideoUploader'
|
||||||
|
import { AudioUploader } from '../Editor/AudioUploader'
|
||||||
import { VideoPlayer } from '../_shared/VideoPlayer'
|
import { VideoPlayer } from '../_shared/VideoPlayer'
|
||||||
import { slugify } from '../../utils/slugify'
|
import { slugify } from '../../utils/slugify'
|
||||||
import { SolidSwiper } from '../_shared/SolidSwiper'
|
import { SolidSwiper } from '../_shared/SolidSwiper'
|
||||||
|
import { DropArea } from '../_shared/DropArea'
|
||||||
|
import { LayoutType, MediaItem } from '../../pages/types'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
shout: Shout
|
shout: Shout
|
||||||
|
@ -66,7 +69,7 @@ export const EditView = (props: Props) => {
|
||||||
layout: props.shout.layout
|
layout: props.shout.layout
|
||||||
})
|
})
|
||||||
|
|
||||||
const mediaItems = createMemo(() => {
|
const mediaItems: Accessor<MediaItem[]> = createMemo(() => {
|
||||||
return JSON.parse(form.media || '[]')
|
return JSON.parse(form.media || '[]')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -125,9 +128,9 @@ export const EditView = (props: Props) => {
|
||||||
setForm('selectedTopics', newSelectedTopics)
|
setForm('selectedTopics', newSelectedTopics)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddImages = (data) => {
|
const handleAddMedia = (data) => {
|
||||||
const newImages = [...mediaItems(), ...data]
|
const newMedia = [...mediaItems(), ...data]
|
||||||
setForm('media', JSON.stringify(newImages))
|
setForm('media', JSON.stringify(newMedia))
|
||||||
}
|
}
|
||||||
const handleSortedImages = (data) => {
|
const handleSortedImages = (data) => {
|
||||||
setForm('media', JSON.stringify(data))
|
setForm('media', JSON.stringify(data))
|
||||||
|
@ -139,15 +142,54 @@ export const EditView = (props: Props) => {
|
||||||
setForm('media', JSON.stringify(copy))
|
setForm('media', JSON.stringify(copy))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleImageChange = (index, value) => {
|
const handleMediaChange = (index, value) => {
|
||||||
const updated = mediaItems().map((item, idx) => (idx === index ? value : item))
|
const updated = mediaItems().map((item, idx) => (idx === index ? value : item))
|
||||||
setForm('media', JSON.stringify(updated))
|
setForm('media', JSON.stringify(updated))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleBaseFieldsChange = (key, value) => {
|
||||||
|
const updated = mediaItems().map((media) => ({ ...media, [key]: value }))
|
||||||
|
setForm('media', JSON.stringify(updated))
|
||||||
|
}
|
||||||
|
|
||||||
|
const articleTitle = () => {
|
||||||
|
switch (props.shout.layout as LayoutType) {
|
||||||
|
case 'audio': {
|
||||||
|
return t('Album name')
|
||||||
|
}
|
||||||
|
case 'image': {
|
||||||
|
return t('Gallery name')
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return t('Header')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pageTitle = () => {
|
||||||
|
switch (props.shout.layout as LayoutType) {
|
||||||
|
case 'audio': {
|
||||||
|
return t('Publish Album')
|
||||||
|
}
|
||||||
|
case 'image': {
|
||||||
|
return t('Create gallery')
|
||||||
|
}
|
||||||
|
case 'video': {
|
||||||
|
return t('Create video')
|
||||||
|
}
|
||||||
|
case 'literature': {
|
||||||
|
return t('New literary work')
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return t('Write an article')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class={styles.container}>
|
<div class={styles.container}>
|
||||||
<Title>{t('Write an article')}</Title>
|
<Title>{pageTitle()}</Title>
|
||||||
<form>
|
<form>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
<button
|
<button
|
||||||
|
@ -167,33 +209,88 @@ export const EditView = (props: Props) => {
|
||||||
[styles.visible]: page().route === 'edit'
|
[styles.visible]: page().route === 'edit'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div class={styles.inputContainer}>
|
<div class={clsx({ [styles.audioHeader]: props.shout.layout === 'audio' })}>
|
||||||
<GrowingTextarea
|
<div class={styles.inputContainer}>
|
||||||
value={(value) => handleTitleInputChange(value)}
|
<GrowingTextarea
|
||||||
class={styles.titleInput}
|
allowEnterKey={true}
|
||||||
placeholder={t('Header')}
|
value={(value) => handleTitleInputChange(value)}
|
||||||
initialValue={form.title}
|
class={styles.titleInput}
|
||||||
maxLength={100}
|
placeholder={articleTitle()}
|
||||||
/>
|
initialValue={form.title}
|
||||||
<Show when={formErrors.title}>
|
maxLength={100}
|
||||||
<div class={styles.validationError}>{formErrors.title}</div>
|
/>
|
||||||
|
|
||||||
|
<Show when={formErrors.title}>
|
||||||
|
<div class={styles.validationError}>{formErrors.title}</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={props.shout.layout === 'audio'}>
|
||||||
|
<div class={styles.additional}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder={t('Artist...')}
|
||||||
|
class={styles.additionalInput}
|
||||||
|
value={mediaItems()[0]?.artist || t('Artist')}
|
||||||
|
onChange={(event) => handleBaseFieldsChange('artist', event.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
class={styles.additionalInput}
|
||||||
|
placeholder={t('Release date...')}
|
||||||
|
onChange={(event) => handleBaseFieldsChange('date', event.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder={t('Genre...')}
|
||||||
|
class={styles.additionalInput}
|
||||||
|
onChange={(event) => handleBaseFieldsChange('genre', event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={props.shout.layout !== 'audio'}>
|
||||||
|
<GrowingTextarea
|
||||||
|
allowEnterKey={false}
|
||||||
|
value={(value) => setForm('subtitle', value)}
|
||||||
|
class={styles.subtitleInput}
|
||||||
|
placeholder={t('Subheader')}
|
||||||
|
initialValue={form.subtitle}
|
||||||
|
maxLength={100}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<Show
|
||||||
|
when={form.coverImageUrl}
|
||||||
|
fallback={
|
||||||
|
<DropArea
|
||||||
|
isSquare={true}
|
||||||
|
placeholder={t('Add cover')}
|
||||||
|
description={
|
||||||
|
<>
|
||||||
|
{t('min. 1400×1400 pix')}
|
||||||
|
<br />
|
||||||
|
{t('jpg, .png, max. 10 mb.')}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
isMultiply={false}
|
||||||
|
fileType={'image'}
|
||||||
|
onUpload={(val) => setForm('coverImageUrl', val[0].url)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class={styles.cover}
|
||||||
|
style={{ 'background-image': `url(${imageProxy(form.coverImageUrl)})` }}
|
||||||
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<GrowingTextarea
|
|
||||||
value={(value) => setForm('subtitle', value)}
|
|
||||||
class={styles.subtitleInput}
|
|
||||||
placeholder={t('Subheader')}
|
|
||||||
initialValue={form.subtitle}
|
|
||||||
maxLength={100}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Show when={props.shout.layout === 'image'}>
|
<Show when={props.shout.layout === 'image'}>
|
||||||
<SolidSwiper
|
<SolidSwiper
|
||||||
editorMode={true}
|
editorMode={true}
|
||||||
images={mediaItems()}
|
images={mediaItems()}
|
||||||
onImageChange={handleImageChange}
|
onImageChange={handleMediaChange}
|
||||||
onImageDelete={(index) => handleImageDelete(index)}
|
onImageDelete={(index) => handleImageDelete(index)}
|
||||||
onImagesAdd={(value) => handleAddImages(value)}
|
onImagesAdd={(value) => handleAddMedia(value)}
|
||||||
onImagesSorted={(value) => handleSortedImages(value)}
|
onImagesSorted={(value) => handleSortedImages(value)}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
|
@ -204,7 +301,7 @@ export const EditView = (props: Props) => {
|
||||||
fallback={
|
fallback={
|
||||||
<VideoUploader
|
<VideoUploader
|
||||||
data={(data) => {
|
data={(data) => {
|
||||||
handleAddImages(data)
|
handleAddMedia(data)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -224,6 +321,14 @@ export const EditView = (props: Props) => {
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
|
<Show when={props.shout.layout === 'audio'}>
|
||||||
|
<AudioUploader
|
||||||
|
audio={mediaItems()}
|
||||||
|
onAudioAdd={(value) => handleAddMedia(value)}
|
||||||
|
onAudioChange={handleMediaChange}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<Editor
|
<Editor
|
||||||
shoutId={props.shout.id}
|
shoutId={props.shout.id}
|
||||||
initialContent={props.shout.body}
|
initialContent={props.shout.body}
|
||||||
|
|
|
@ -58,6 +58,24 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.square {
|
||||||
|
.field {
|
||||||
|
@include font-size(1.4rem);
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
width: 228px;
|
||||||
|
height: 228px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin-top: 8px;
|
||||||
|
opacity: 0.3;
|
||||||
|
color: var(--default-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slide {
|
@keyframes slide {
|
||||||
|
|
|
@ -6,14 +6,16 @@ import { useLocalize } from '../../../context/localize'
|
||||||
import { validateFiles } from '../../../utils/validateFile'
|
import { validateFiles } from '../../../utils/validateFile'
|
||||||
import type { FileTypeToUpload } from '../../../pages/types'
|
import type { FileTypeToUpload } from '../../../pages/types'
|
||||||
import { handleFileUpload } from '../../../utils/handleFileUpload'
|
import { handleFileUpload } from '../../../utils/handleFileUpload'
|
||||||
|
import { UploadedFile } from '../../../pages/types'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
class?: string
|
class?: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
description?: string | JSX.Element
|
|
||||||
fileType: FileTypeToUpload
|
|
||||||
isMultiply: boolean
|
isMultiply: boolean
|
||||||
onUpload: (value: string[]) => void
|
fileType: FileTypeToUpload
|
||||||
|
onUpload: (value: UploadedFile[]) => void
|
||||||
|
description?: string | JSX.Element
|
||||||
|
isSquare?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DropArea = (props: Props) => {
|
export const DropArea = (props: Props) => {
|
||||||
|
@ -26,15 +28,16 @@ export const DropArea = (props: Props) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
const results: string[] = []
|
const results: UploadedFile[] = []
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const result = await handleFileUpload(file)
|
const result = await handleFileUpload(file)
|
||||||
results.push(result.url)
|
results.push(result)
|
||||||
}
|
}
|
||||||
props.onUpload(results)
|
props.onUpload(results)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setDropAreaError('Error')
|
setLoading(false)
|
||||||
|
setDropAreaError(t('Upload error'))
|
||||||
console.error('[runUpload]', error)
|
console.error('[runUpload]', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +84,7 @@ export const DropArea = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={clsx(styles.DropArea, props.class)}>
|
<div class={clsx(styles.DropArea, props.class, props.isSquare && styles['square'])}>
|
||||||
<div
|
<div
|
||||||
class={clsx(styles.field, { [styles.active]: dragActive() })}
|
class={clsx(styles.field, { [styles.active]: dragActive() })}
|
||||||
onDragEnter={handleDrag}
|
onDragEnter={handleDrag}
|
||||||
|
@ -91,11 +94,14 @@ export const DropArea = (props: Props) => {
|
||||||
onClick={handleDropFieldClick}
|
onClick={handleDropFieldClick}
|
||||||
>
|
>
|
||||||
<div class={styles.text}>{loading() ? 'Loading...' : props.placeholder}</div>
|
<div class={styles.text}>{loading() ? 'Loading...' : props.placeholder}</div>
|
||||||
|
<Show when={!loading() && props.isSquare && props.description}>
|
||||||
|
<div class={styles.description}>{props.description}</div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<Show when={dropAreaError()}>
|
<Show when={dropAreaError()}>
|
||||||
<div class={styles.error}>{dropAreaError()}</div>
|
<div class={styles.error}>{dropAreaError()}</div>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={!dropAreaError() && props.description}>
|
<Show when={!dropAreaError() && props.description && !props.isSquare}>
|
||||||
<div class={styles.description}>{props.description}</div>
|
<div class={styles.description}>{props.description}</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,7 @@ type Props = {
|
||||||
initialValue?: string
|
initialValue?: string
|
||||||
value: (string) => void
|
value: (string) => void
|
||||||
maxLength?: number
|
maxLength?: number
|
||||||
|
allowEnterKey: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GrowingTextarea = (props: Props) => {
|
export const GrowingTextarea = (props: Props) => {
|
||||||
|
@ -36,7 +37,7 @@ export const GrowingTextarea = (props: Props) => {
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class={clsx(styles.textInput, props.class)}
|
class={clsx(styles.textInput, props.class)}
|
||||||
value={props.initialValue}
|
value={props.initialValue}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={props.allowEnterKey ? handleKeyDown : null}
|
||||||
onInput={(event) => handleChangeValue(event)}
|
onInput={(event) => handleChangeValue(event)}
|
||||||
onChange={(event) => props.value(event.target.value)}
|
onChange={(event) => props.value(event.target.value)}
|
||||||
placeholder={props.placeholder}
|
placeholder={props.placeholder}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createEffect, createSignal, For, Match, Show, Switch, on } from 'solid-js'
|
import { createEffect, createSignal, For, Match, Show, Switch, on } from 'solid-js'
|
||||||
import { MediaItem } from '../../../pages/types'
|
import { MediaItem, UploadedFile } from '../../../pages/types'
|
||||||
import { Icon } from '../Icon'
|
import { Icon } from '../Icon'
|
||||||
import { Popover } from '../Popover'
|
import { Popover } from '../Popover'
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
@ -17,6 +17,7 @@ import { Loading } from '../Loading'
|
||||||
import { imageProxy } from '../../../utils/imageProxy'
|
import { imageProxy } from '../../../utils/imageProxy'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import styles from './Swiper.module.scss'
|
import styles from './Swiper.module.scss'
|
||||||
|
import { composeMediaItems } from '../../../utils/composeMediaItems'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
images: MediaItem[]
|
images: MediaItem[]
|
||||||
|
@ -27,17 +28,6 @@ type Props = {
|
||||||
onImageChange?: (index: number, value: MediaItem) => void
|
onImageChange?: (index: number, value: MediaItem) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const composeMediaItem = (value) => {
|
|
||||||
return value.map((url) => {
|
|
||||||
return {
|
|
||||||
url: url,
|
|
||||||
source: '',
|
|
||||||
title: '',
|
|
||||||
body: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
register()
|
register()
|
||||||
|
|
||||||
SwiperCore.use([Pagination, Navigation, Manipulation])
|
SwiperCore.use([Pagination, Navigation, Manipulation])
|
||||||
|
@ -47,7 +37,6 @@ export const SolidSwiper = (props: Props) => {
|
||||||
const [loading, setLoading] = createSignal(false)
|
const [loading, setLoading] = createSignal(false)
|
||||||
const [slideIndex, setSlideIndex] = createSignal(0)
|
const [slideIndex, setSlideIndex] = createSignal(0)
|
||||||
|
|
||||||
const dropAreaRef: { current: HTMLElement } = { current: null }
|
|
||||||
const mainSwipeRef: { current: SwiperRef } = { current: null }
|
const mainSwipeRef: { current: SwiperRef } = { current: null }
|
||||||
const thumbSwipeRef: { current: SwiperRef } = { current: null }
|
const thumbSwipeRef: { current: SwiperRef } = { current: null }
|
||||||
|
|
||||||
|
@ -78,8 +67,8 @@ export const SolidSwiper = (props: Props) => {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleDropAreaUpload = (value: string[]) => {
|
const handleDropAreaUpload = (value: UploadedFile[]) => {
|
||||||
props.onImagesAdd(composeMediaItem(value))
|
props.onImagesAdd(composeMediaItems(value))
|
||||||
swipeToUploaded()
|
swipeToUploaded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +97,7 @@ export const SolidSwiper = (props: Props) => {
|
||||||
const result = await handleFileUpload(file)
|
const result = await handleFileUpload(file)
|
||||||
results.push(result.url)
|
results.push(result.url)
|
||||||
}
|
}
|
||||||
props.onImagesAdd(composeMediaItem(results))
|
props.onImagesAdd(composeMediaItems(results))
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
swipeToUploaded()
|
swipeToUploaded()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -146,7 +135,6 @@ export const SolidSwiper = (props: Props) => {
|
||||||
<div class={styles.container}>
|
<div class={styles.container}>
|
||||||
<Show when={props.editorMode && props.images.length === 0}>
|
<Show when={props.editorMode && props.images.length === 0}>
|
||||||
<DropArea
|
<DropArea
|
||||||
ref={(el) => (dropAreaRef.current = el)}
|
|
||||||
fileType="image"
|
fileType="image"
|
||||||
isMultiply={true}
|
isMultiply={true}
|
||||||
placeholder={t('Add images')}
|
placeholder={t('Add images')}
|
||||||
|
@ -212,6 +200,7 @@ export const SolidSwiper = (props: Props) => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<GrowingTextarea
|
<GrowingTextarea
|
||||||
|
allowEnterKey={true}
|
||||||
class={styles.descriptionText}
|
class={styles.descriptionText}
|
||||||
placeholder={t('Enter image description')}
|
placeholder={t('Enter image description')}
|
||||||
initialValue={slide.body}
|
initialValue={slide.body}
|
||||||
|
|
|
@ -42,10 +42,10 @@ export const CreatePage = () => {
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#">
|
<div class={styles.link} onClick={() => handleCreate('audio')}>
|
||||||
<Icon name="create-music" class={styles.icon} />
|
<Icon name="create-music" class={styles.icon} />
|
||||||
<div>{t('music')}</div>
|
<div>{t('music')}</div>
|
||||||
</a>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class={styles.link} onClick={() => handleCreate('video')}>
|
<div class={styles.link} onClick={() => handleCreate('video')}>
|
||||||
|
|
|
@ -34,11 +34,23 @@ export type UploadFile = {
|
||||||
|
|
||||||
export type LayoutType = 'article' | 'audio' | 'video' | 'image' | 'literature'
|
export type LayoutType = 'article' | 'audio' | 'video' | 'image' | 'literature'
|
||||||
|
|
||||||
export type FileTypeToUpload = 'image' | 'video' | 'doc'
|
export type FileTypeToUpload = 'image' | 'video' | 'doc' | 'audio'
|
||||||
|
|
||||||
|
export type AudioDescription = {
|
||||||
|
date?: string
|
||||||
|
genre?: string
|
||||||
|
artist?: string
|
||||||
|
lyrics?: string
|
||||||
|
}
|
||||||
|
|
||||||
export type MediaItem = {
|
export type MediaItem = {
|
||||||
url: string
|
url: string
|
||||||
title: string
|
title: string
|
||||||
body: string
|
body: string
|
||||||
source?: string
|
source?: string
|
||||||
|
} & AudioDescription
|
||||||
|
|
||||||
|
export type UploadedFile = {
|
||||||
|
url: string
|
||||||
|
originalFilename: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
--icon-filter: invert(0);
|
--icon-filter: invert(0);
|
||||||
--icon-filter-hover: invert(1);
|
--icon-filter-hover: invert(1);
|
||||||
--editor-bubble-menu-background: #fff;
|
--editor-bubble-menu-background: #fff;
|
||||||
|
--blue-link: #2638d9;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-editor-dark-mode='true'] {
|
[data-editor-dark-mode='true'] {
|
||||||
|
|
|
@ -246,7 +246,6 @@ export const apiClient = {
|
||||||
},
|
},
|
||||||
createArticle: async ({ article }: { article: ShoutInput }): Promise<Shout> => {
|
createArticle: async ({ article }: { article: ShoutInput }): Promise<Shout> => {
|
||||||
const response = await privateGraphQLClient.mutation(createArticle, { shout: article }).toPromise()
|
const response = await privateGraphQLClient.mutation(createArticle, { shout: article }).toPromise()
|
||||||
console.log('!!! [createArticle]:', response.data)
|
|
||||||
return response.data.createShout.shout
|
return response.data.createShout.shout
|
||||||
},
|
},
|
||||||
updateArticle: async ({
|
updateArticle: async ({
|
||||||
|
|
10
src/utils/composeMediaItems.ts
Normal file
10
src/utils/composeMediaItems.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export const composeMediaItems = (value) => {
|
||||||
|
return value.map((fileData) => {
|
||||||
|
return {
|
||||||
|
url: fileData.url,
|
||||||
|
source: '',
|
||||||
|
title: fileData.originalFilename,
|
||||||
|
body: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -23,6 +23,10 @@ export const validateFiles = (fileType: FileTypeToUpload, files: UploadFile[]):
|
||||||
isValid = docExtension ? docExtensions.has(docExtension) : false
|
isValid = docExtension ? docExtensions.has(docExtension) : false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'audio': {
|
||||||
|
isValid = file.file.type.startsWith('audio/')
|
||||||
|
break
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
isValid = false
|
isValid = false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user