Port client to TypeScript

Co-authored-by: Jack W. <Jack5079@users.noreply.github.com>
This commit is contained in:
split / May 2024-03-27 21:47:33 -07:00
parent 77cfa27615
commit f441e06a21
30 changed files with 619 additions and 329 deletions

264
package-lock.json generated
View file

@ -30,6 +30,7 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.4.6",
"@tsconfig/svelte": "^4.0.1",
"@types/bytes": "^3.1.1",
"@types/cookie-parser": "^1.4.3",
"@types/formidable": "^3.4.5",
@ -37,10 +38,12 @@
"discord-api-types": "^0.37.61",
"sass": "^1.57.1",
"svelte": "^3.55.1",
"svelte-preprocess": "^5.1.3",
"tslib": "^2.6.2",
"vite": "^4.5.0"
},
"engines": {
"node": ">=v16.11"
"node": ">=v21"
}
},
"node_modules/@esbuild/linux-x64": {
@ -158,6 +161,12 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/@tsconfig/svelte": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-4.0.1.tgz",
"integrity": "sha512-B+XlGpmuAQzJqDoBATNCvEPqQg0HkO7S8pM14QDI5NsmtymzRexQ1N+nX2H6RTtFbuFgaZD4I8AAi8voGg0GLg==",
"dev": true
},
"node_modules/@types/body-parser": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
@ -246,6 +255,12 @@
"@types/node": "*"
}
},
"node_modules/@types/pug": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
"integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
"dev": true
},
"node_modules/@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@ -319,6 +334,12 @@
"form-data": "^4.0.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -351,6 +372,16 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
@ -363,6 +394,15 @@
"node": ">=8"
}
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@ -445,6 +485,12 @@
"node": ">=16"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/concat-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
@ -566,6 +612,15 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
"integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/dezalgo": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
@ -602,6 +657,12 @@
"node": ">= 0.8"
}
},
"node_modules/es6-promise": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
"integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==",
"dev": true
},
"node_modules/esbuild": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
@ -816,10 +877,19 @@
"node": ">= 0.6"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.1.3",
@ -834,6 +904,26 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@ -846,6 +936,12 @@
"node": ">= 6"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@ -916,6 +1012,16 @@
"integrity": "sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==",
"dev": true
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@ -1048,6 +1154,27 @@
"node": ">= 0.6"
}
},
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
@ -1207,6 +1334,15 @@
"node": ">= 0.8"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
@ -1342,6 +1478,18 @@
"node": ">=8.10.0"
}
},
"node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/rollup": {
"version": "3.29.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
@ -1382,6 +1530,18 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sander": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
"integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==",
"dev": true,
"dependencies": {
"es6-promise": "^3.1.2",
"graceful-fs": "^4.1.3",
"mkdirp": "^0.5.1",
"rimraf": "^2.5.2"
}
},
"node_modules/sass": {
"version": "1.57.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz",
@ -1459,6 +1619,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sorcery": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
"integrity": "sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.14",
"buffer-crc32": "^0.2.5",
"minimist": "^1.2.0",
"sander": "^0.5.0"
},
"bin": {
"sorcery": "bin/sorcery"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@ -1497,6 +1672,18 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
"dev": true,
"dependencies": {
"min-indent": "^1.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/svelte": {
"version": "3.55.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
@ -1518,6 +1705,69 @@
"svelte": "^3.19.0 || ^4.0.0"
}
},
"node_modules/svelte-preprocess": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.3.tgz",
"integrity": "sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@types/pug": "^2.0.6",
"detect-indent": "^6.1.0",
"magic-string": "^0.30.5",
"sorcery": "^0.11.0",
"strip-indent": "^3.0.0"
},
"engines": {
"node": ">= 16.0.0",
"pnpm": "^8.0.0"
},
"peerDependencies": {
"@babel/core": "^7.10.2",
"coffeescript": "^2.5.1",
"less": "^3.11.3 || ^4.0.0",
"postcss": "^7 || ^8",
"postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
"pug": "^3.0.0",
"sass": "^1.26.8",
"stylus": "^0.55.0",
"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
"typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"coffeescript": {
"optional": true
},
"less": {
"optional": true
},
"postcss": {
"optional": true
},
"postcss-load-config": {
"optional": true
},
"pug": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -1538,6 +1788,12 @@
"node": ">=0.6"
}
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",

View file

@ -39,6 +39,7 @@
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.4.6",
"@tsconfig/svelte": "^4.0.1",
"@types/bytes": "^3.1.1",
"@types/cookie-parser": "^1.4.3",
"@types/formidable": "^3.4.5",
@ -46,6 +47,8 @@
"discord-api-types": "^0.37.61",
"sass": "^1.57.1",
"svelte": "^3.55.1",
"svelte-preprocess": "^5.1.3",
"tslib": "^2.6.2",
"vite": "^4.5.0"
}
}

View file

@ -22,7 +22,7 @@
content="width=device-width, initial-scale=1.0, user-scalable=0"
/>
<script type="module" src="./svelte/index.js"></script>
<script type="module" src="./svelte/index.ts"></script>
<title>monofile</title>

View file

@ -24,7 +24,7 @@ export default async function ServeError(
// serve error
return ctx.req.header("accept").includes("text/html") ? ctx.html(
return ctx.req.header("accept")?.includes("text/html") ? ctx.html(
errorPage
.replaceAll("$code", code.toString())
.replaceAll("$text", reason),

View file

@ -1,4 +1,4 @@
.pulldown_display[name=accounts] {
.pulldown_display[data-name=accounts] {
.notLoggedIn {
.container_div {
position:absolute;
@ -185,3 +185,41 @@
}
}
}
@keyframes bounce {
0% {
top: 0.25em;
}/*
25% {
top: 0.25em;
}
75% {
top: -0.25em;
}*/
100% {
top: -0.25em;
}
}
.loader {
i {
font-style: normal;
position: relative;
animation-name: bounce;
animation-duration: 500ms;
animation-iteration-count: infinite;
animation-direction: alternate;
top:0.25em;
&:nth-of-type(1) {
animation-delay: 0ms;
}
&:nth-of-type(2) {
animation-delay: 125ms;
}
&:nth-of-type(3) {
animation-delay: 250ms;
}
}
}

View file

@ -1,4 +1,4 @@
.pulldown_display[name=files] {
.pulldown_display[data-name=files] {
.notLoggedIn {
position:absolute;
top:50%;

View file

@ -1,4 +1,4 @@
.pulldown_display[name=help] {
.pulldown_display[data-name=help] {
overflow-y:auto;

View file

@ -13,7 +13,7 @@
span {
position:relative;
&._add_files_txt {
&.add_files_txt {
font-size:16px;
top:-4px;
left:10px;
@ -29,7 +29,7 @@
@media screen and (max-width:500px) {
font-size: 40px;
span._add_files_txt {
span.add_files_txt {
font-size:20px;
top:-6px;
left:10px;
@ -45,7 +45,7 @@
flex-direction:row;
column-gap:10px;
button, input[type=text] {
button, input[type=text], input[type=submit] {
background-color:#333333;
color:#DDDDDD;
border:none;
@ -63,7 +63,7 @@
}
}
button {
button, input[type=submit] {
cursor:pointer;
&:hover {

View file

@ -50,7 +50,7 @@
overflow:auto;
}
button {
button, input[type=submit] {
cursor:pointer;
background-color:#393939;
color:#DDDDDD;

View file

@ -1,19 +1,13 @@
<script>
<script lang="ts">
import { onMount } from "svelte";
import Topbar from "./elem/Topbar.svelte";
import PulldownManager from "./elem/PulldownManager.svelte";
import UploadWindow from "./elem/UploadWindow.svelte";
import { pulldownManager } from "./elem/stores.mjs";
import { pulldownManager } from "./elem/stores.js";
/**
* @type Topbar
*/
let topbar;
let topbar: Topbar;
/**
* @type PulldownManager
*/
let pulldown;
let pulldown: PulldownManager;
onMount(() => {
pulldownManager.set(pulldown)

View file

@ -1,4 +1,4 @@
<script context="module">
<script context="module" lang="ts">
import { writable } from "svelte/store";
// can't find a better way to do this
@ -13,10 +13,10 @@
.set("help",Help)
.set("files",Files)
export const pulldownOpen = writable(false);
export const pulldownOpen = writable<string|false>(false);
</script>
<script>
<script lang="ts">
import { onMount } from "svelte";
import { fade, scale } from "svelte/transition";
@ -24,7 +24,7 @@
return $pulldownOpen
}
export function openPulldown(name) {
export function openPulldown(name: string) {
pulldownOpen.set(name)
}

View file

@ -1,14 +1,11 @@
<script>
<script lang="ts">
import { circOut } from "svelte/easing";
import { scale } from "svelte/transition";
import PulldownManager, {pulldownOpen} from "./PulldownManager.svelte";
import { account } from "./stores.mjs";
import { _void } from "./transition/_void";
import { account } from "./stores.js";
import { _void } from "./transition/_void.js";
/**
* @type PulldownManager
*/
export let pulldown;
export let pulldown: PulldownManager;
</script>
<div id="topbar">
@ -23,7 +20,7 @@
<!-- too lazy to make this better -->
<button class="menuBtn" on:click={() => pulldown.openPulldown("files")}>files</button>
<button class="menuBtn" on:click={() => pulldown.openPulldown("account")}>{$account.username ? `@${$account.username}` : "account"}</button>
<button class="menuBtn" on:click={() => pulldown.openPulldown("account")}>{$account?.username ? `@${$account.username}` : "account"}</button>
<button class="menuBtn" on:click={() => pulldown.openPulldown("help")}>help</button>
<div /> <!-- not sure what's offcenter but something is

View file

@ -1,9 +1,9 @@
<script>
<script lang="ts">
import { _void } from "./transition/_void.js"
import { padding_scaleY } from "./transition/padding_scaleY.js"
import { fade } from "svelte/transition"
import { circIn, circOut } from "svelte/easing"
import { serverStats, refresh_stats, account } from "./stores.mjs"
import { serverStats, refresh_stats, account } from "./stores.js"
import bytes from "bytes"
import AttachmentZone from "./uploader/AttachmentZone.svelte"
@ -14,52 +14,44 @@
// uploads
interface Upload {
file: string | File
params: {
uploadId?: string
}
uploadStatus: {
fileId?: string,
error?: string,
}
maximized?: boolean,
viewingUrl?: boolean
}
let attachmentZone
let uploads = {}
let uploads: Record<string, Upload> = {}
let uploadInProgress = false
let notificationPermission =
globalThis?.Notification?.permission ?? "denied"
let handle_file_upload = (ev) => {
if (ev.detail.type == "clone") {
uploads[Math.random().toString().slice(2)] = {
type: "clone",
name: ev.detail.url,
url: ev.detail.url,
let handle_file_upload = (file: Event & { detail: File|string }) => {
params: {
uploadId: "",
},
uploads[Math.random().toString().slice(2)] = {
file: file.detail,
uploadStatus: {
fileId: null,
error: null,
},
}
params: {
uploadId: "",
},
uploads = uploads
} else if (ev.detail.type == "upload") {
ev.detail.files.forEach((v, x) => {
uploads[Math.random().toString().slice(2)] = {
type: "upload",
name: v.name,
file: v,
params: {
uploadId: "",
},
uploadStatus: {
fileId: null,
error: null,
},
}
})
uploads = uploads
uploadStatus: {}
}
uploads = uploads
}
let handle_fetch_promise = (x, prom) => {
let handle_fetch_promise = (x: string, prom: Promise<Response>) => {
return prom
.then(async (res) => {
let txt = await res.text()
@ -81,8 +73,8 @@
],
}).addEventListener(
"notificationclick",
({ action }) => {
if (action === "open") {
(event) => {
if ("action" in event && event.action === "open") {
open(
"/download/" +
uploads[x].uploadStatus.fileId
@ -115,7 +107,7 @@
let hdl = () => {
let fd = new FormData()
if (v.params.uploadId) fd.append("uploadId", v.params.uploadId)
fd.append("file", v.type == "clone" ? v.url : v.file)
fd.append("file", v.file)
return handle_fetch_promise(x,fetch("/api/v1/file",{
method: "PUT",
@ -130,10 +122,10 @@
// animation
function fileTransition(node) {
function fileTransition(node: HTMLElement) {
return {
duration: 300,
css: (t) => {
css: (t: number) => {
let eased = circOut(t)
return `
@ -175,7 +167,7 @@
</h1>
<p style:color="#999999">
<span class="number"
>{$serverStats.version ? `v${$serverStats.version}` : "•••"}</span
>{$serverStats?.version ? `v${$serverStats?.version}` : "•••"}</span
>&nbsp;&nbsp;&nbsp;&nbsp;Discord based file sharing
</p>
@ -195,12 +187,9 @@
: ""}
>
<h2>
{upload[1].name}
{typeof upload[1].file == "string" ? upload[1].file : upload[1].file.name}
<span style:color="#999999" style:font-weight="400"
>{upload[1].type}{@html upload[1].type == "upload"
? `&nbsp;(${bytes(upload[1].file.size)})`
: ""}</span
>
>{@html typeof upload[1].file == "string" ? "clone" : `upload&nbsp;(${bytes(upload[1].file.size)})`}</span>
</h2>
{#if upload[1].maximized && !uploadInProgress}
@ -319,7 +308,7 @@
{#if uploadInProgress == false}
<!-- if required for upload, check if logged in -->
{#if ($serverStats.accounts || {}).requiredForUpload ? !!$account.username : true}
{#if $serverStats?.accounts?.requiredForUpload ? !!$account?.username : true}
<AttachmentZone
bind:this={attachmentZone}
on:addFiles={handle_file_upload}
@ -352,12 +341,12 @@
<p style:color="#999999" style:text-align="center">
Hosting <span class="number" style:font-weight="600"
>{$serverStats.files || "•••"}</span
>{$serverStats?.files ?? "•••"}</span
>
files — Maximum filesize is
<span class="number" style:font-weight="600">
{
$serverStats.maxDiscordFiles
$serverStats?.maxDiscordFiles
? bytes($serverStats.maxDiscordFileSize * $serverStats.maxDiscordFiles)
: "•••"
}</span>

View file

@ -1,28 +1,30 @@
<script>
<script lang="ts">
import { fade, slide } from "svelte/transition";
interface BaseModalOption {
name:string,
icon:string,
id: string | number | symbol | boolean
}
let activeModal;
let modalResults;
type ModalOption = BaseModalOption & {inputSettings: {password?: boolean}, id: any} | BaseModalOption & { description: string }
/**
*
* @param mdl {name:string,icon:string,description:string,id:string}[]
* @returns Promise
*/
export function picker(title,mdl) {
type ModalOptions = ModalOption[]
type OptionPickerReturns = {selected: any} & Record<any,any> | null
let activeModal: {resolve: (val: OptionPickerReturns) => void, title: string, modal: ModalOptions } | undefined;
let modalResults: Record<string | number | symbol, string> = {};
export function picker(title: string,mdl: ModalOptions): Promise<OptionPickerReturns> {
if (activeModal) forceCancel()
return new Promise((resolve,reject) => {
return new Promise<OptionPickerReturns>((resolve,reject) => {
activeModal = {
resolve,
title,
modal:mdl
}
modalResults = {
}
modalResults = {}
})
}
@ -30,7 +32,7 @@
if (activeModal && activeModal.resolve) {
activeModal.resolve(null)
}
activeModal = null
activeModal = undefined
}
</script>
@ -46,9 +48,9 @@
</div>
{#each activeModal.modal as option (option.id)}
{#if option.inputSettings}
{#if "inputSettings" in option}
<div class="inp">
<img src={option.icon} alt={option.id}>
<img src={option.icon} alt={option.id.toString()}>
<!-- i have to do this stupidness because of svelte but -->
<!-- its reason for blocking this is pretty good sooooo -->
@ -60,8 +62,8 @@
{/if}
</div>
{:else}
<button on:click={() => {activeModal.resolve({...modalResults,selected:option.id});activeModal=null;modalResults=null;}}>
<img src={option.icon} alt={option.id}>
<button on:click={() => {activeModal?.resolve({...modalResults,selected:option.id});activeModal=undefined;modalResults={};}}>
<img src={option.icon} alt={option.id.toString()}>
<p>{option.name}<span><br />{option.description}</span></p>
</button>
{/if}

View file

@ -1,7 +1,8 @@
import { fetchAccountData, account, refreshNeeded } from "../stores.mjs"
import { fetchAccountData, account, refreshNeeded } from "../stores"
import { get } from "svelte/store";
import type OptionPicker from "./OptionPicker.svelte";
export function deleteAccount(optPicker) {
export function deleteAccount(optPicker: OptionPicker) {
optPicker.picker("What should we do with your files?",[
{
name: "Delete my files",
@ -56,7 +57,7 @@ export function deleteAccount(optPicker) {
})
}
export function userChange(optPicker) {
export function userChange(optPicker: OptionPicker) {
optPicker.picker("Change username",[
{
name: "New username",
@ -86,7 +87,7 @@ export function userChange(optPicker) {
})
}
export function forgotPassword(optPicker) {
export function forgotPassword(optPicker: OptionPicker) {
optPicker.picker("Forgot your password?",[
{
name: "Username",
@ -115,7 +116,7 @@ export function forgotPassword(optPicker) {
})
}
export function emailPotentialRemove(optPicker) {
export function emailPotentialRemove(optPicker: OptionPicker) {
optPicker.picker("What would you like to do?",[
{
name: "Set a new email",
@ -148,7 +149,7 @@ export function emailPotentialRemove(optPicker) {
})
}
export function emailChange(optPicker) {
export function emailChange(optPicker: OptionPicker) {
optPicker.picker("Change email",[
{
name: "New email",
@ -177,7 +178,7 @@ export function emailChange(optPicker) {
})
}
export function pwdChng(optPicker) {
export function pwdChng(optPicker: OptionPicker) {
optPicker.picker("Change password",[
{
name: "New password",
@ -209,7 +210,7 @@ export function pwdChng(optPicker) {
})
}
export function customcss(optPicker) {
export function customcss(optPicker: OptionPicker) {
optPicker.picker("Set custom CSS",[
{
name: "Enter a file ID",
@ -250,7 +251,7 @@ export function customcss(optPicker) {
}
export function embedColor(optPicker) {
export function embedColor(optPicker: OptionPicker) {
optPicker.picker("Set embed color",[
{
name: "FFFFFF",
@ -290,7 +291,7 @@ export function embedColor(optPicker) {
}
export function embedSize(optPicker) {
export function embedSize(optPicker: OptionPicker) {
optPicker.picker("Set embed image size",[
{
name: "Large",

View file

@ -1,7 +1,8 @@
import { fetchAccountData, fetchFilePointers, account } from "../stores.mjs"
import { fetchAccountData, fetchFilePointers, account } from "../stores"
import { get } from "svelte/store";
import type OptionPicker from "./OptionPicker.svelte";
export function pwdReset(optPicker) {
export function pwdReset(optPicker: OptionPicker) {
optPicker.picker("Reset password",[
{
name: "Target user",
@ -39,7 +40,7 @@ export function pwdReset(optPicker) {
})
}
export function chgOwner(optPicker) {
export function chgOwner(optPicker: OptionPicker) {
optPicker.picker("Transfer file ownership",[
{
name: "File ID",
@ -75,7 +76,7 @@ export function chgOwner(optPicker) {
})
}
export function chgId(optPicker) {
export function chgId(optPicker: OptionPicker) {
optPicker.picker("Change file ID",[
{
name: "Target file",
@ -111,7 +112,7 @@ export function chgId(optPicker) {
})
}
export function delFile(optPicker) {
export function delFile(optPicker: OptionPicker) {
optPicker.picker("Delete file",[
{
name: "File ID",
@ -140,7 +141,7 @@ export function delFile(optPicker) {
})
}
export function elevateUser(optPicker) {
export function elevateUser(optPicker: OptionPicker) {
optPicker.picker("Elevate user",[
{
name: "Username",
@ -171,7 +172,7 @@ export function elevateUser(optPicker) {
// im really lazy so i just stole this from account.js
export function deleteAccount(optPicker) {
export function deleteAccount(optPicker: OptionPicker) {
optPicker.picker("What should we do with the target account's files?",[
{
name: "Delete files",

View file

@ -1,5 +1,7 @@
import { fetchAccountData, fetchFilePointers, account } from "../stores.mjs"
import { fetchAccountData, fetchFilePointers, account } from "../stores"
import { get } from "svelte/store";
import type OptionPicker from "./OptionPicker.svelte"
import type { FilePointer } from "../../../server/lib/files";
export let options = {
FV: [
@ -51,7 +53,7 @@ export let options = {
]
}
export function dfv(optPicker) {
export function dfv(optPicker: OptionPicker) {
optPicker.picker("Default file visibility",options.FV).then((exp) => {
if (exp && exp.selected) {
fetch(`/auth/dfv`,{method:"POST", body:JSON.stringify({
@ -68,21 +70,21 @@ export function dfv(optPicker) {
})
}
export function update_all_files(optPicker) {
export function update_all_files(optPicker: OptionPicker) {
optPicker.picker("You sure?",[
{
name: "Yeah",
icon: "/static/assets/icons/update.svg",
description: `This will make all of your files ${get(account).defaultFileVisibility || "public"}`,
description: `This will make all of your files ${get(account)?.defaultFileVisibility || "public"}`,
id: true
}
]).then((exp) => {
if (exp && exp.selected) {
fetch(`/files/manage`,{method:"POST", body:JSON.stringify({
target:get(account).files,
target:get(account)?.files,
action: "changeFileVisibility",
value: get(account).defaultFileVisibility
value: get(account)?.defaultFileVisibility
})}).then((response) => {
if (response.status != 200) {
@ -95,7 +97,7 @@ export function update_all_files(optPicker) {
})
}
export function fileOptions(optPicker,file) {
export function fileOptions(optPicker: OptionPicker, file: FilePointer & {id:string}) {
optPicker.picker(file.filename,[
{
name: file.tag ? "Remove tag" : "Tag file",

View file

@ -1,26 +1,26 @@
<script>
<script lang="ts">
import Pulldown from "./Pulldown.svelte"
import { padding_scaleY } from "../transition/padding_scaleY"
import { circIn,circOut } from "svelte/easing"
import { account, fetchAccountData, serverStats, refreshNeeded } from "../stores.mjs";
import { account, fetchAccountData, serverStats, refreshNeeded } from "../stores";
import { fade } from "svelte/transition";
import OptionPicker from "../prompts/OptionPicker.svelte";
import * as accOpts from "../prompts/account";
import * as uplOpts from "../prompts/uploads";
import * as admOpts from "../prompts/admin";
let targetAction
let inProgress
let authError
let targetAction: "login"|"create"
let inProgress: boolean
let authError:{status:number,message:string}|undefined
let pwErr
let pwErr: HTMLDivElement
let optPicker;
let optPicker: OptionPicker;
// lazy
let username
let password
let username: string
let password: string
let execute = () => {
if (inProgress) return
@ -43,7 +43,7 @@
}
})
} else {
authError = null, username = "", password = "";
authError = undefined, username = "", password = "";
fetchAccountData();
}
}).catch(() => {})
@ -66,55 +66,7 @@
<Pulldown name="accounts">
<OptionPicker bind:this={optPicker} />
{#if Object.keys($account).length == 0}
<div class="notLoggedIn" transition:fade={{duration:200}}>
<div class="container_div">
<h1>monofile <span style:color="#999999">accounts</span></h1>
<p class="flavor">Gain control of your uploads.</p>
{#if targetAction}
<div class="fields" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
{#if !$serverStats.accounts.registrationEnabled && targetAction == "create"}
<div class="pwError">
<div style:background-color="#554C33">
<p>Account registration has been disabled by this instance's owner</p>
</div>
</div>
{/if}
{#if authError}
<div class="pwError" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
<div bind:this={pwErr}>
<p><strong>{authError.status}</strong> {authError.message}</p>
</div>
</div>
{/if}
<input placeholder="username" type="text" bind:value={username}>
<input placeholder="password" type="password" bind:value={password}>
<button on:click={execute}>{ inProgress ? "• • •" : (targetAction=="login" ? "Log in" : "Create account") }</button>
{#if targetAction == "login"}
<button class="flavor" on:click={() => accOpts.forgotPassword(optPicker)}>I forgot my password</button>
{/if}
</div>
{:else}
<div class="lgBtnContainer" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
<button on:click={() => targetAction="login"}>Log in</button>
<button on:click={() => targetAction="create"}>Sign up</button>
</div>
{/if}
</div>
</div>
{:else}
{#if $account}
<div class="loggedIn" transition:fade={{duration:200}}>
<h1>
Hey there, <span class="monospace">@{$account.username}</span>
@ -131,7 +83,7 @@
<p>Change username</p>
</button>
<button on:click={() => ($account.email ? accOpts.emailPotentialRemove : accOpts.emailChange)(optPicker)}>
<button on:click={() => ($account?.email ? accOpts.emailPotentialRemove : accOpts.emailChange)(optPicker)}>
<img src="/static/assets/icons/mail.svg" alt="change email">
<p>Change email{#if $account.email}<span class="monospaceText"><br />{$account.email}</span>{/if}</p>
</button>
@ -182,7 +134,7 @@
</button>
{#if $refreshNeeded}
<button on:click={() => window.location.reload(true)} transition:fade={{duration: 200}}>
<button on:click={() => window.location.reload()} transition:fade={{duration: 200}}>
<img src="/static/assets/icons/refresh.svg" alt="refresh">
<p>Refresh<span><br />Changes were made which require a refresh</span></p>
</button>
@ -194,12 +146,12 @@
<button on:click={() => fetch(`/auth/logout_sessions`,{method:"POST"}).then(() => fetchAccountData())}>
<img src="/static/assets/icons/logout_all.svg" alt="logout_all">
<p>Log out all sessions<span><br />{$account.sessionCount} session(s) active</span></p>
<p>Log out all sessions<span><br />{$account?.sessionCount} session(s) active</span></p>
</button>
<button on:click={() => fetch(`/auth/logout`,{method:"POST"}).then(() => fetchAccountData())}>
<img src="/static/assets/icons/logout.svg" alt="logout">
<p>Log out<span><br />Session expires {new Date($account.sessionExpires).toLocaleDateString()}</span></p>
<p>Log out<span><br />Session expires {new Date($account?.sessionExpires).toLocaleDateString()}</span></p>
</button>
{#if $account.admin}
@ -242,6 +194,50 @@
<p style="font-size:12px;color:#AAAAAA;text-align:center;" class="monospace"><br />{$account.id}</p>
</div>
</div>
{:else}
<div class="notLoggedIn" transition:fade={{duration:200}}>
<div class="container_div">
<h1>monofile <span style:color="#999999">accounts</span></h1>
<p class="flavor">Gain control of your uploads.</p>
{#if targetAction}
<div class="fields" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
{#if !$serverStats?.accounts.registrationEnabled && targetAction == "create"}
<div class="pwError">
<div style:background-color="#554C33">
<p>Account registration has been disabled by this instance's owner</p>
</div>
</div>
{/if}
{#if authError}
<div class="pwError" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
<div bind:this={pwErr}>
<p><strong>{authError.status}</strong> {authError.message}</p>
</div>
</div>
{/if}
<input placeholder="username" type="text" bind:value={username}>
<input placeholder="password" type="password" bind:value={password}>
<button on:click={execute}>{@html inProgress ? "<span class=loader><i>•</i> <i>•</i> <i>•</i></span>" : (targetAction=="login" ? "Log in" : "Create account") }</button>
{#if targetAction == "login"}
<button class="flavor" on:click={() => accOpts.forgotPassword(optPicker)}>I forgot my password</button>
{/if}
</div>
{:else}
<div class="lgBtnContainer" out:padding_scaleY|local={{easingFunc:circIn}} in:padding_scaleY|local>
<button on:click={() => targetAction="login"}>Log in</button>
<button on:click={() => targetAction="create"}>Sign up</button>
</div>
{/if}
</div>
</div>
{/if}
</Pulldown>

View file

@ -1,13 +1,13 @@
<script>
<script lang="ts">
import Pulldown from "./Pulldown.svelte";
import { account, fetchFilePointers, files, pulldownManager } from "../stores.mjs";
import { account, fetchFilePointers, files, pulldownManager } from "../stores.js";
import { fade } from "svelte/transition";
import { flip } from "svelte/animate";
import { fileOptions } from "../prompts/uploads";
import OptionPicker from "../prompts/OptionPicker.svelte";
let picker;
let picker: OptionPicker;
let query = "";
fetchFilePointers();
@ -17,48 +17,47 @@
<OptionPicker bind:this={picker} />
{#if !$account.username}
{#if $account?.username}<div class="loggedIn">
<input type="text" placeholder={`Search ${$files.length} file(s)`} class="searchBar" bind:value={query}>
<div class="fileList">
<!-- Probably wildly inefficient but who cares, I just wanna get this over with -->
{#each $files.filter(f => f&&(f.filename.toLowerCase().includes(query.toLowerCase()) || f.id.toLowerCase().includes(query.toLowerCase()) || f.tag?.includes(query.toLowerCase()))) as file (file.id)}
<div class="flFile" transition:fade={{duration:200}} animate:flip={{duration:200}}>
<button class="hitbox" on:click={() => window.open(`/download/${file.id}`)}></button> <!-- this is bad, but I'm lazy -->
<div class="flexCont">
<div class="fileInfo">
<h2>{file.filename}</h2>
<p class="detail">
<img src="/static/assets/icons/{file.visibility || "public"}.svg" alt={file.visibility||"public"} />&nbsp;
<span class="number">{file.id}</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="cd">{file.mime.split(";")[0]}</span>
{#if file.reserved}
<br />
<img src="/static/assets/icons/update.svg" alt="uploading"/>&nbsp;
Uploading...
{/if}
{#if file.tag}
<br />
<img src="/static/assets/icons/tag.svg" alt="tag"/>&nbsp;
<span class="cd">{file.tag}</span>
{/if}
</p>
</div>
<button class="more" on:click={() => fileOptions(picker, file)}>
<img src="/static/assets/icons/more.svg" alt="more" />
</button>
</div>
</div>
{/each}
</div>
</div>
{:else}
<div class="notLoggedIn">
<div style:height="10px" />
<p class="flavor">Log in to view uploads</p>
<button on:click={$pulldownManager.openPulldown("account")}>OK</button>
<div style:height="14px" />
</div>
{:else}
<div class="loggedIn">
<input type="text" placeholder={`Search ${$files.length} file(s)`} class="searchBar" bind:value={query}>
<div class="fileList">
<!-- Probably wildly inefficient but who cares, I just wanna get this over with -->
{#each $files.filter(f => f&&(f.filename.toLowerCase().includes(query.toLowerCase()) || f.id.toLowerCase().includes(query.toLowerCase()) || f.tag?.includes(query.toLowerCase()))) as file (file.id)}
<div class="flFile" transition:fade={{duration:200}} animate:flip={{duration:200}}>
<button class="hitbox" on:click={window.open(`/download/${file.id}`)}></button> <!-- this is bad, but I'm lazy -->
<div class="flexCont">
<div class="fileInfo">
<h2>{file.filename}</h2>
<p class="detail">
<img src="/static/assets/icons/{file.visibility || "public"}.svg" alt={file.visibility||"public"} />&nbsp;
<span class="number">{file.id}</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="cd">{file.mime.split(";")[0]}</span>
{#if file.reserved}
<br />
<img src="/static/assets/icons/update.svg" alt="uploading"/>&nbsp;
Uploading...
{/if}
{#if file.tag}
<br />
<img src="/static/assets/icons/tag.svg" alt="tag"/>&nbsp;
<span class="cd">{file.tag}</span>
{/if}
</p>
</div>
<button class="more" on:click={fileOptions(picker, file)}>
<img src="/static/assets/icons/more.svg" alt="more" />
</button>
</div>
</div>
{/each}
</div>
</div>
{/if}
</Pulldown>

View file

@ -1,13 +1,13 @@
<script>
<script lang=ts>
import { fade } from "svelte/transition";
export let name;
export let name: string;
</script>
<div
class="pulldown_display"
name={name}
data-name={name}
transition:fade={{duration:150}}
>
<slot />

View file

@ -1,17 +1,22 @@
import { writable } from "svelte/store"
//import type Pulldown from "./pulldowns/Pulldown.svelte"
import type { SvelteComponent } from "svelte"
import type { Account } from "../../server/lib/accounts"
import type cfg from "../../../config.json"
import type { FilePointer } from "../../server/lib/files"
export let refreshNeeded = writable(false)
export let pulldownManager = writable(0)
export let account = writable({})
export let serverStats = writable({})
export let files = writable([])
export let pulldownManager = writable<SvelteComponent>()
export let account = writable<Account & {sessionCount: number, sessionExpires: number}|undefined>()
export let serverStats = writable<typeof cfg & {version: string, files: number} | undefined>()
export let files = writable<(FilePointer & {id:string})[]>([])
export let fetchAccountData = function() {
fetch("/auth/me").then(async (response) => {
if (response.status == 200) {
account.set(await response.json())
} else {
account.set({})
account.set(undefined)
}
}).catch((err) => { console.error(err) })
}

View file

@ -1,20 +0,0 @@
import { circIn, circOut } from "svelte/easing"
export function _void(node, { duration, easingFunc, op, prop, rTarg }) {
let rect = node.getBoundingClientRect()
return {
duration: duration||300,
css: t => {
let eased = (easingFunc || circIn)(t)
return `
white-space: nowrap;
${prop||"height"}: ${(eased)*(rect[rTarg||prop||"height"])}px;
padding: 0px;
opacity:${eased};
overflow: clip;
`
}
}
}

View file

@ -0,0 +1,23 @@
import { circIn, circOut } from "svelte/easing"
export function _void(
node: HTMLElement,
options?: { duration?:number, easingFunc?: (a:number)=>number, prop?:string, rTarg?: "height"|"width"}
) {
const { duration = 300, easingFunc = circIn, prop, rTarg } = options ?? {}
let rect = node.getBoundingClientRect()
return {
duration,
css: (t: number) => {
let eased = easingFunc(t)
return `
white-space: nowrap;
${prop||"height"}: ${(eased)*(rect[rTarg || (prop && prop in rect) ? prop as keyof Omit<DOMRect, "toJSON"> : "height"])}px;
padding: 0px;
opacity:${eased};
overflow: clip;
`
}
}
}

View file

@ -1,18 +0,0 @@
import { circIn, circOut } from "svelte/easing"
export function padding_scaleY(node, { duration, easingFunc, padY, padX, op }) {
let rect = node.getBoundingClientRect()
return {
duration: duration||300,
css: t => {
let eased = (easingFunc || circOut)(t)
return `
height: ${eased*(rect.height-(padY||0))}px;
${padX&&padY ? `padding: ${(eased)*(padY)}px ${(padX)}px;` : ""}
${op ? `opacity: ${eased};` : ""}
`
}
}
}

View file

@ -0,0 +1,21 @@
import { circIn, circOut } from "svelte/easing"
function padding_scaleY(node: HTMLElement, options?: { duration?: number, easingFunc?: (a: number) => number, padY?: number, padX?: number, op?: boolean }) {
const { duration = 300, easingFunc = circOut, padY, padX, op } = options ?? {}
let rect = node.getBoundingClientRect()
return {
duration,
css: (t:number) => {
let eased = easingFunc(t)
return `
height: ${eased*(rect.height-(padY||0))}px;
${padX&&padY ? `padding: ${(eased)*(padY)}px ${(padX)}px;` : ""}
${op ? `opacity: ${eased};` : ""}
`
}
}
}
export {padding_scaleY}

View file

@ -1,56 +1,35 @@
<script>
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { circIn, circOut } from "svelte/easing"
import { fade } from "svelte/transition";
import { circOut } from "svelte/easing"
import { _void } from "../transition/_void"
let uploadTypes = {
files: 1,
clone: 2
enum UploadTypes {
None,
Files,
Clone
}
let uploadType = undefined
let uploadType: UploadTypes = UploadTypes.None
let dispatch = createEventDispatcher();
// file upload
/**
* @type HTMLInputElement
*/
let fileUpload;
$: {
if (fileUpload) {
fileUpload.addEventListener("change",() => {
dispatch("addFiles",{
type: "upload",
files: Array.from(fileUpload.files)
})
uploadType = undefined
})
}
let files: FileList | undefined
$: if (files) {
[...files].forEach(file=>dispatch("addFiles", file))
uploadType = UploadTypes.None
}
// file clone
/**
* @type HTMLButtonElement
*/
let cloneButton;
/**
* @type HTMLInputElement
*/
let cloneUrlTextbox;
let cloneUrlTextbox: HTMLInputElement;
let cloneForm: HTMLFormElement;
$: {
if (cloneButton && cloneUrlTextbox) {
cloneButton.addEventListener("click",() => {
if (cloneForm && cloneUrlTextbox) {
cloneForm.addEventListener("submit",(e) => {
e.preventDefault()
if (cloneUrlTextbox.value) {
dispatch("addFiles",{
type: "clone",
url: cloneUrlTextbox.value
})
uploadType = undefined;
dispatch("addFiles",cloneUrlTextbox.value)
uploadType = UploadTypes.None;
} else {
cloneUrlTextbox.animate([
{"transform":"translateX(0px)"},
@ -68,26 +47,26 @@
<div id="add_new_files" transition:_void={{duration:200}}>
<p>
+<span class="_add_files_txt">add files</span>
+<span class="add_files_txt">add files</span>
</p>
{#if !uploadType}
{#if uploadType == UploadTypes.None}
<div id="file_add_btns" out:_void in:_void={{easingFunc:circOut}}>
<button on:click={() => uploadType = uploadTypes.files} >upload files...</button>
<button on:click={() => uploadType = uploadTypes.clone} >clone url...</button>
<button on:click={() => uploadType = UploadTypes.Files} >upload files...</button>
<button on:click={() => uploadType = UploadTypes.Clone} >clone url...</button>
</div>
{:else}
{#if uploadType == uploadTypes.files}
{#if uploadType == UploadTypes.Files}
<div id="file_add_btns" out:_void in:_void={{easingFunc:circOut}}>
<div class="fileUpload">
<p>click/tap to browse<br/>or drag files into this box</p>
<input type="file" multiple bind:this={fileUpload}>
<input type="file" multiple bind:files={files}>
</div>
</div>
{:else if uploadType == uploadTypes.clone}
<div id="file_add_btns" out:_void in:_void={{easingFunc:circOut}}>
{:else if uploadType == UploadTypes.Clone}
<form id="file_add_btns" out:_void in:_void={{easingFunc:circOut}} bind:this={cloneForm}>
<input placeholder="url" type="text" bind:this={cloneUrlTextbox}>
<button style:flex-basis="30%" bind:this={cloneButton}>add file</button>
</div>
<input type="submit" value="add file" style:flex-basis="30%">
</form>
{/if}
{/if}
</div>

1
src/svelte/global.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="svelte" />

18
src/svelte/tsconfig.json Normal file
View file

@ -0,0 +1,18 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"include": ["**/*"],
"compilerOptions": {
"target": "ESNext",
"outDir": "../../dist/static/vite",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"isolatedModules": true,
"moduleResolution": "bundler"
},
"references": [
{ "path": "../../tsconfig.json" }
]
}

View file

@ -1,5 +1,6 @@
import { defineConfig } from "vite"
import { svelte } from "@sveltejs/vite-plugin-svelte"
import autoPreprocess from "svelte-preprocess"
import { resolve } from "path"
export default defineConfig({
root: "./src",
@ -14,5 +15,7 @@ export default defineConfig({
},
},
},
plugins: [svelte({})],
plugins: [svelte({
preprocess: autoPreprocess()
})],
})