Merge pull request #2102 from maxberger/master

UI: More compact layour, auto-scroll to user in shares
This commit is contained in:
Peter Bieringer
2026-04-24 07:04:26 +02:00
committed by GitHub
3 changed files with 160 additions and 43 deletions

View File

@@ -19,14 +19,17 @@ main {
.container { .container {
height: auto; height: auto;
min-height: 450px; min-height: 500px;
width: 350px; width: 95%;
max-width: 450px;
box-sizing: border-box;
transition: .2s; transition: .2s;
overflow: hidden; overflow: hidden;
padding: 20px 40px; padding: 1ex 1em 1ex 1em;
background: #fff; background: rgb(250, 250, 250);
border: 1px solid #dadce0; border: 1px solid #dadce0;
border-radius: 8px; border-radius: 8px;
box-shadow: 2px 2px 3px #0000001a;
display: block; display: block;
flex-shrink: 0; flex-shrink: 0;
margin: 0 auto; margin: 0 auto;
@@ -39,12 +42,55 @@ main {
color: #484848; color: #484848;
} }
.container h2 { table {
margin: 0;
width: 100%; width: 100%;
border-collapse: collapse;
margin: 1ex 0;
}
th,
td {
text-align: left; text-align: left;
color: #484848; font-size: 0.8em;
font-size: 14pt; padding: 0.2ex 0.2em;
}
tr {
border-bottom: 1px solid #dadce0;
}
tr:last-child {
border-bottom: none;
}
th {
font-size: 0.8em;
color: #666;
font-weight: normal;
text-transform: uppercase;
}
td.fill {
width: 100%;
}
details {
border: 1px solid #dadce0;
border-radius: 0.7ex;
padding: 0.5ex 0.7ex;
margin-bottom: 1ex;
background: white;
}
summary {
font-weight: bold;
cursor: pointer;
padding: 0.2ex 0;
outline: none;
}
summary:hover {
color: #2494fe;
} }
#loginscene .infcloudlink { #loginscene .infcloudlink {
@@ -394,6 +440,7 @@ select {
padding-left: 15px; padding-left: 15px;
padding-right: 15px; padding-right: 15px;
outline: none !important; outline: none !important;
box-sizing: border-box;
} }
input.inline { input.inline {
@@ -402,7 +449,7 @@ input.inline {
input[type=text], input[type=text],
input[type=password] { input[type=password] {
width: calc(100% - 30px); width: 100%;
} }
input:active, input:active,
@@ -421,13 +468,22 @@ input:focus-visible {
clear: both; clear: both;
} }
td .url-input-wrapper input {
min-height: 4ex;
}
.url-input-wrapper input { .url-input-wrapper input {
flex: 1; flex: 1;
min-width: 0; min-width: 40px;
padding-right: 45px !important; min-height: 6ex;
padding-left: 0.5em;
padding-top: 0.5ex;
padding-bottom: 0.5ex;
padding-right: 22px !important;
box-sizing: border-box; box-sizing: border-box;
width: 100% !important; width: 100% !important;
margin-bottom: 0 !important; margin-bottom: 0 !important;
height: 2ex;
} }
#collectionsscene article .url-input-wrapper { #collectionsscene article .url-input-wrapper {
@@ -436,7 +492,7 @@ input:focus-visible {
.copy-btn { .copy-btn {
flex: none; flex: none;
margin-left: -38px; margin-left: -30px;
background: transparent; background: transparent;
border: none; border: none;
color: #2494fe; color: #2494fe;
@@ -539,6 +595,9 @@ button.inline {
padding-right: 1px; padding-right: 1px;
min-width: 2em; min-width: 2em;
margin: 0; margin: 0;
display: flex;
align-items: center;
justify-content: center;
} }
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
@@ -547,22 +606,10 @@ button.inline {
flex-wrap: nowrap; flex-wrap: nowrap;
} }
#collectionsscene article {
height: auto;
min-height: 375px;
}
.container {
max-width: 280px !important;
}
#collectionsscene ul { #collectionsscene ul {
visibility: visible !important; visibility: visible !important;
} }
#logoutview span {
padding: 0 5px;
}
} }
.pill { .pill {
@@ -573,12 +620,12 @@ button.inline {
line-height: 1; line-height: 1;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
vertical-align: baseline; vertical-align: middle;
border-radius: 0.375rem; border-radius: 0.7ex;
background-color: #6c757d; background-color: #6c757d;
color: #fff; color: #fff;
margin: 0 2px; margin: 0 2px;
width: 1.4em; min-width: 1.3em;
} }
.hidden { .hidden {

View File

@@ -158,10 +158,11 @@
<tbody> <tbody>
<tr data-name="sharetokenrowtemplate" class="hidden"> <tr data-name="sharetokenrowtemplate" class="hidden">
<td> <td>
<button type="button" class="blue inline" data-name="edit" title="Edit Share"><img src="css/icons/edit.svg" <button type="button" class="blue inline" data-name="edit" title="Edit Share"><img
class="small_icon" alt="Edit"></button> src="css/icons/edit.svg" class="small_icon" alt="Edit"></button>
</td> </td>
<td><span class="pill" data-name="rw">rw</span><span class="pill" data-name="ro">ro</span><span data-name="conversion"></span></td> <td><span class="pill" data-name="rw">rw</span><span class="pill" data-name="ro">ro</span><span
data-name="conversion"></span></td>
<td> <td>
<div class="url-input-wrapper"> <div class="url-input-wrapper">
<input type="text" data-name="pathortoken" value="" readonly="" class="inline selectall"> <input type="text" data-name="pathortoken" value="" readonly="" class="inline selectall">
@@ -169,14 +170,17 @@
</div> </div>
</td> </td>
<td> <td>
<button type="button" class="red inline" data-name="delete" title="Delete Share"><img src="css/icons/delete.svg" <button type="button" class="red inline" data-name="delete" title="Delete Share"><img
class="small_icon" alt="Delete"></button> src="css/icons/delete.svg" class="small_icon" alt="Delete"></button>
</td> </td>
</tr> </tr>
<tr> <tr>
<td><button type="button" class="blue inline" data-name="sharebytoken" title="New Share by Token"><img src="css/icons/new.svg" <td><button type="button" class="blue inline" data-name="sharebytoken" title="New Share by Token"><img
class="small_icon" alt="New Share by Token"></button> src="css/icons/new.svg" class="small_icon" alt="New Share by Token"></button>
</td> </td>
<td></td>
<td class="fill"></td>
<td></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -189,10 +193,11 @@
<tbody> <tbody>
<tr data-name="sharemaprowtemplate" class="hidden"> <tr data-name="sharemaprowtemplate" class="hidden">
<td> <td>
<button type="button" class="blue inline" data-name="edit" title="Edit Share"><img src="css/icons/edit.svg" <button type="button" class="blue inline" data-name="edit" title="Edit Share"><img
class="small_icon" alt="Edit"></button> src="css/icons/edit.svg" class="small_icon" alt="Edit"></button>
</td> </td>
<td><span class="pill" data-name="rw">rw</span><span class="pill" data-name="ro">ro</span><span data-name="conversion"></span></td> <td><span class="pill" data-name="rw">rw</span><span class="pill" data-name="ro">ro</span><span
data-name="conversion"></span></td>
<td> <td>
<div class="url-input-wrapper"> <div class="url-input-wrapper">
<input type="text" data-name="pathortoken" value="" readonly="" class="inline selectall"> <input type="text" data-name="pathortoken" value="" readonly="" class="inline selectall">
@@ -200,8 +205,8 @@
</div> </div>
</td> </td>
<td> <td>
<button type="button" class="red inline" data-name="delete" title="Delete Share"><img src="css/icons/delete.svg" <button type="button" class="red inline" data-name="delete" title="Delete Share"><img
class="small_icon" alt="Delete"></button> src="css/icons/delete.svg" class="small_icon" alt="Delete"></button>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -210,6 +215,9 @@
<img src="css/icons/new.svg" class="small_icon" alt="New Share by Map"> <img src="css/icons/new.svg" class="small_icon" alt="New Share by Map">
</button> </button>
</td> </td>
<td></td>
<td class="fill"></td>
<td></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -25,6 +25,9 @@ import { SERVER } from "../constants.js";
* Handles the display of URLs in input fields. * Handles the display of URLs in input fields.
*/ */
export class UrlTextHandler { export class UrlTextHandler {
/** @type {?HTMLCanvasElement} */
static _canvas = null;
/** /**
* @param {HTMLInputElement} element The input element to handle. * @param {HTMLInputElement} element The input element to handle.
* @param {HTMLButtonElement} copyButton The button element for copying. * @param {HTMLButtonElement} copyButton The button element for copying.
@@ -32,9 +35,14 @@ export class UrlTextHandler {
constructor(element, copyButton) { constructor(element, copyButton) {
this._element = element; this._element = element;
this._copyButton = copyButton; this._copyButton = copyButton;
this._userIndex = -1;
this._element.addEventListener("focusin", () => { this._element.addEventListener("focusin", () => {
this._element.setSelectionRange(0, 99999); this._element.setSelectionRange(0, 99999);
this._updateScroll();
});
this._element.addEventListener("focusout", () => {
this._updateScroll();
}); });
if (this._copyButton) { if (this._copyButton) {
@@ -42,6 +50,37 @@ export class UrlTextHandler {
} }
} }
_updateScroll() {
setTimeout(() => {
this._updateScroll_now();
}, 0);
}
/**
* Update the scroll position of the input field.
*/
_updateScroll_now() {
if (this._userIndex === -1) {
this._element.scrollLeft = this._element.scrollWidth;
return;
}
let scrollPos = this._element.scrollWidth;
const value = this._element.value;
const style = window.getComputedStyle(this._element);
if (!UrlTextHandler._canvas) {
UrlTextHandler._canvas = document.createElement("canvas");
}
const context = UrlTextHandler._canvas.getContext("2d");
if (context) {
context.font = style.font;
const prefix = value.substring(0, this._userIndex);
scrollPos = context.measureText(prefix).width;
}
this._element.scrollLeft = scrollPos;
}
/** /**
* Copy the current value of the input to the clipboard. * Copy the current value of the input to the clipboard.
*/ */
@@ -76,10 +115,33 @@ export class UrlTextHandler {
} else { } else {
this._element.value = href; this._element.value = href;
} }
this._update_username_index();
this._updateScroll();
}
// Needs timeout to work correctly. /**
setTimeout(() => { * Update the username index.
this._element.scrollLeft = this._element.scrollWidth; */
}, 0); _update_username_index() {
const value = this._element.value;
this._userIndex = -1;
try {
const url = new URL(value);
const path = url.pathname;
const parts = path.split("/").filter(p => p !== "");
if (parts.length >= 2) {
const user = parts[parts.length - 2];
const search = "/" + user + "/";
const pathIndex = path.lastIndexOf(search);
if (pathIndex !== -1) {
const fullIndex = value.lastIndexOf(path);
if (fullIndex !== -1) {
this._userIndex = fullIndex + pathIndex;
}
}
}
} catch (e) {
this._userIndex = -1;
}
} }
} }