Merge pull request #14 from jolavillette/feature/responsive-webui

Phone adaptations inside the pages
This commit is contained in:
defnax 2026-08-15 15:53:28 +02:00 committed by GitHub
commit a2352e2963
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 358 additions and 4 deletions

View File

@ -218,7 +218,9 @@ const CommentsTable = () => {
oninit: (v) => {},
view: (v) =>
m('table.comments', [
m('tr', [
// See table.mails: the header row is tagged so the small screen
// stylesheet can card-ify the data rows only.
m('tr.comments-head', [
m('th', ''),
m('th', 'Comment'),
m('th', 'Author'),

View File

@ -114,7 +114,7 @@ const navbar = () => {
? 'Connected to RetroShare Core'
: 'Connection Lost',
}),
m('span.webui-version', { style: { fontSize: '0.7em' } }, 'v139'),
m('span.webui-version', { style: { fontSize: '0.7em' } }, 'v140'),
m('i.fas.fa-sync-alt.refresh-icon', {
style: { cursor: 'pointer', fontSize: '0.8em' },
onclick: () => window.location.reload(true),
@ -236,7 +236,7 @@ const MobileStatus = () => {
m('small', statusbar.formatBytes(state.totalOut)),
]),
]),
m('.mobile-status-sheet__version', 'WebUI v139'),
m('.mobile-status-sheet__version', 'WebUI v140'),
])),
];
},

View File

@ -0,0 +1,304 @@
// -----------------------------------------------------------------------------
// Phone adaptations, inside the pages.
//
// Loaded LAST from main.scss so these rules win over the page stylesheets
// without needing !important. Everything here is scoped inside the `mobile`
// (phone, portrait or landscape) or `touch` (no hover) mixins from
// abstracts/_mixins.scss, so the desktop rendering is untouched.
//
// Neither the navigation, nor the two-pane pages, nor the mail list are this
// file's business: main.js renders a bottom tab bar and a status sheet of its
// own on a phone, components/_navbar.scss hides the desktop rail below 700px,
// the network, people and chat pages switch to master-detail on their own
// through a .mobile-detail-open class driven from their own state, and
// pages/_mail.scss turns table.mails into a card list. This file starts where
// all of that stops -- inside the pages, still laid out for a wide screen:
// * multi column grids and wide tables collapse to a single column,
// * anything only reachable on :hover gets a touch fallback.
//
// The page stylesheets carry a small screen layout of their own, with their own
// breakpoints (768px for network and people, 899px for the chat, 700px for
// files and config). This file neither repeats nor edits them: it takes over
// below the phone breakpoint, and the few places where a tablet compromise does
// not survive on a phone are overridden with !important and carry a comment
// saying which rule they beat; there should be no other !important here.
// -----------------------------------------------------------------------------
@use 'abstracts' as *;
/* =========================================================================
1. Multi column grids collapse
========================================================================= */
@include mobile {
/* Network friend details, identity details, location cards */
.network-detail-view {
gap: 1rem;
.detail-header {
flex-direction: column;
align-items: flex-start;
/* gap comes from pages/_network.scss, which sets it !important */
padding-bottom: 1rem;
.detail-title h2 {
font-size: 1.35rem;
}
.detail-actions {
flex-wrap: wrap;
width: 100%;
}
}
.detail-section {
padding: 0.875rem;
.info-grid {
grid-template-columns: 1fr;
row-gap: 0.25rem;
.info-label {
margin-top: 0.5rem;
}
}
}
.locations-grid {
grid-template-columns: 1fr;
}
.location-card .loc-body {
grid-template-columns: 1fr;
}
}
/* Config: proxy rows are 160+220+220px wide, i.e. 620px minimum */
.proxy-row {
grid-template-columns: 1fr;
gap: 0.35rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid #e2e8f0;
}
/* Generic two column form grid used across the config pages */
.grid-2col {
grid-template-columns: 1fr;
& input[type='checkbox'] {
margin-top: 0;
}
}
/* People: identity detail card */
.identity {
margin: 0.5rem;
padding: 0.75rem;
& .details {
grid-template-columns: 1fr;
grid-row-gap: 0.125rem;
}
}
/* Mail composer popup: pages/_mail.scss already grows it to 95% of the
viewport on a small screen, which on a phone leaves a useless 5% frame
around a form that needs every pixel. Its rule is !important, hence these. */
.composePopupOverlay .composePopup {
width: 100% !important;
height: 100% !important;
inset: 0;
& > .widget {
padding: 0.75rem;
border-radius: 0;
}
& .close-btn {
top: 0.5rem;
right: 0.5rem;
}
}
.widget {
padding: 0.5rem;
}
.widget-half {
max-width: 100%;
}
}
/* =========================================================================
2. Tables
========================================================================= */
@include mobile {
/* The global `table-layout: fixed` plus percentage column widths squeeze
every column below readability. Let the content drive the width instead. */
table {
table-layout: auto;
font-size: 1rem;
}
table td,
table th {
word-break: break-word;
}
/* Files tables: drop the hardcoded 50% name column */
table.myfiles th:nth-child(2),
table.friendsfiles th:nth-child(2) {
width: auto;
}
/* --- Channel comments: 8 columns, unusable as a table -----------------
The header row carries .comments-head (set in channels_util.js) because
mithril builds the DOM through the DOM API and therefore does not get the
implicit <tbody> the HTML parser would insert: neither tr:first-child nor a
`> tbody >` path is reliable here. */
table.comments {
/* The table box has to go: a <tr> switched to block/flex inside a table box
is wrapped back into an anonymous table cell and re-sized by the column
algorithm, which shreds the row. */
display: block;
& tbody {
display: block;
}
& tr.comments-head {
display: none;
}
& tr:not(.comments-head) {
display: block;
padding: 0.5rem 0;
& > td {
display: block;
text-align: start;
padding: 0.125rem 0;
}
/* author / date / score / votes fold into one small meta line */
& > td:nth-child(n + 3) {
display: inline-block;
margin-right: 0.75rem;
font-size: 0.8rem;
color: #64748b;
}
& > td:nth-child(5)::before {
content: 'Score: ';
}
& > td:nth-child(6)::before {
content: '\002B06 ';
}
& > td:nth-child(7)::before {
content: '\002B07 ';
}
}
}
}
/* =========================================================================
3. Statusbar
========================================================================= */
@include mobile {
.statusbar {
/* padding and font-size are already shrunk by components/_statusbar.scss */
gap: 0.5rem;
/* Keep every counter reachable rather than dropping data */
overflow-x: auto;
white-space: nowrap;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
&-left,
&-right {
flex-shrink: 0;
gap: 0.5rem !important;
}
&-divider {
display: none;
}
}
}
/* =========================================================================
4. Forms and inputs
========================================================================= */
@include mobile {
/* iOS zooms the whole page in when a focused field is under 16px */
input[type='text'],
input[type='password'],
input[type='number'],
input[type='search'],
input[type='email'],
select,
textarea {
font-size: 16px;
}
input {
&.stretched,
&.searchbar {
width: 100%;
}
&.small {
max-width: 100%;
}
}
/* Page level sidebars turned into a horizontal tab strip by _navbar.scss:
make the targets tall enough for a finger. */
.sidebar a {
min-height: $touch-target;
display: inline-flex !important;
align-items: center;
}
/* Tooltips are anchored with a fixed -120px offset, which pushes them off
screen on a narrow viewport */
.tooltiptext {
left: 0;
margin-left: 0;
min-width: 0;
width: max-content;
max-width: 80vw;
}
}
/* =========================================================================
5. Touch fallbacks (independent of the viewport width)
========================================================================= */
@include touch {
/* Hover-revealed helpers are unreachable without a pointer */
.tooltip .tooltiptext {
/* still hidden by default, but reachable by tapping the element */
&:focus,
&:focus-within {
visibility: visible;
}
}
.tooltip:focus-within .tooltiptext {
visibility: visible;
}
/* Recipient autocomplete stays open while the field has focus; the list is
only kept alive by :hover on desktop. */
.compose-mail__recipients .recipients__input:focus-within .recipients__input-list {
display: flex;
}
}

View File

@ -3,6 +3,29 @@
// -----------------------------------------------------------------------------
@use 'sass:color';
@use './colors' as *;
@use './variables' as *;
/// Responsive helpers
/// Always go through these instead of hardcoding a pixel value, so the
/// breakpoints stay defined in a single place (abstracts/_variables.scss).
/// The phone layer: portrait, plus landscape, which is wide enough to escape a
/// width-only breakpoint but far too short for a two-pane layout. Between the
/// two lies the small screen layout of the page stylesheets, which this file
/// deliberately leaves alone.
@mixin mobile {
@media (max-width: $bp-mobile), (max-width: $bp-narrow) and (max-height: $bp-short) {
@content;
}
}
/// Devices without a real pointer: :hover never fires reliably, so anything
/// that is only reachable on hover has to be made permanently visible here.
@mixin touch {
@media (hover: none), (pointer: coarse) {
@content;
}
}
/// Button Mixin
@mixin button($bg-color) {

View File

@ -4,3 +4,20 @@
$FontPath: './webfonts' !default;
$FontName: 'Roboto' !default;
$FontVersion: '3.008' !default;
// Responsive breakpoints.
//
// The page stylesheets already carry a small screen layout of their own: they
// stack the two panes of network and people below 768px, the chat below 899px,
// and turn the mail sidebar into a drawer. _responsive.scss does not redo that
// work and does not edit those files; it adds the phone layer underneath, and
// overrides them only where a tablet compromise does not survive a phone.
//
// $bp-mobile therefore means "a phone", not "a small window".
$bp-mobile: 700px !default; // phone, portrait
$bp-narrow: 899px !default; // widest small screen rule of the page stylesheets
$bp-short: 500px !default; // a phone held sideways is wide, but never tall
// Minimum comfortable size for a touch target (Material/HIG recommend ~44px)
$touch-target: 2.75rem !default;

View File

@ -5,6 +5,10 @@ General site-wide rules
#main {
height: 100vh;
/* Mobile browsers count the collapsible URL bar in 100vh, which pushes the
statusbar and the bottom navigation off screen. dvh follows the visible
viewport; the vh above stays as the fallback. */
height: 100dvh;
}
/* Main base div for tabs used by m.route */

View File

@ -20,3 +20,7 @@
// 6. Page-specific styles
@use 'pages';
// 7. Small screen / touch adaptations. Must stay last: it overrides the
// layouts defined above without relying on !important.
@use 'responsive';

File diff suppressed because one or more lines are too long