Commit ·
5ec3025
1
Parent(s): 3acde0a
Mobile chain-detail layout: stack in portrait, wrap long content
Browse filesStack Chain Identifier and chain metadata vertically in portrait; give the
detail cards min-w-0 and wrap long sequences / experimental details / binders
so they no longer overflow their cards on narrow screens.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
frontend/src/components/datasets/protein/ExperimentalConditionsPanel.tsx
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
*/
|
| 5 |
import React, { useState } from 'react';
|
| 6 |
import { ChainMetadata } from '../../../types/protein';
|
| 7 |
-
import { formatText, formatPH, formatTemperature, truncateText } from '../../../utils/format';
|
| 8 |
|
| 9 |
interface ExperimentalConditionsPanelProps {
|
| 10 |
metadata: ChainMetadata | null;
|
|
@@ -89,12 +89,12 @@ const ExperimentalConditionsPanel: React.FC<ExperimentalConditionsPanelProps> =
|
|
| 89 |
<dd className="text-sm font-semibold text-slate-900">{formattedTemp}</dd>
|
| 90 |
</div>
|
| 91 |
|
| 92 |
-
<div className="
|
| 93 |
{formattedDetails && (
|
| 94 |
-
<div className="experimental-item experimental-details">
|
| 95 |
<dt className="text-xs font-medium text-slate-500 uppercase tracking-wider">Details</dt>
|
| 96 |
<dd className="text-sm font-semibold text-slate-900">
|
| 97 |
-
<div className="details-content">
|
| 98 |
{showFullDetails ? formattedDetails : detailsPreview}
|
| 99 |
</div>
|
| 100 |
{hasLongDetails && (
|
|
|
|
| 4 |
*/
|
| 5 |
import React, { useState } from 'react';
|
| 6 |
import { ChainMetadata } from '../../../types/protein';
|
| 7 |
+
import { formatText, formatPH, formatTemperature, truncateText } from '../../../utils/format';
|
| 8 |
|
| 9 |
interface ExperimentalConditionsPanelProps {
|
| 10 |
metadata: ChainMetadata | null;
|
|
|
|
| 89 |
<dd className="text-sm font-semibold text-slate-900">{formattedTemp}</dd>
|
| 90 |
</div>
|
| 91 |
|
| 92 |
+
<div className="py-2 border-b border-slate-100">
|
| 93 |
{formattedDetails && (
|
| 94 |
+
<div className="experimental-item experimental-details min-w-0">
|
| 95 |
<dt className="text-xs font-medium text-slate-500 uppercase tracking-wider">Details</dt>
|
| 96 |
<dd className="text-sm font-semibold text-slate-900">
|
| 97 |
+
<div className="details-content wrap-anywhere mt-1">
|
| 98 |
{showFullDetails ? formattedDetails : detailsPreview}
|
| 99 |
</div>
|
| 100 |
{hasLongDetails && (
|
frontend/src/components/datasets/protein/ProteinDashboard.tsx
CHANGED
|
@@ -275,8 +275,8 @@ export function ProteinDashboard() {
|
|
| 275 |
<main className="space-y-6">
|
| 276 |
{hasActiveChainSearch && activeChainMetadata ? (
|
| 277 |
<>
|
| 278 |
-
{/* First Row: Search + Chain Information (
|
| 279 |
-
<div className="grid gap-6 items-stretch
|
| 280 |
<section className="min-w-0 h-full border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| 281 |
<FilterPanel
|
| 282 |
filterOptions={filterOptions}
|
|
@@ -304,18 +304,18 @@ export function ProteinDashboard() {
|
|
| 304 |
|
| 305 |
{/* Second Row: Sequence, Experimental Conditions, Binders (3 columns, equal height) */}
|
| 306 |
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
| 307 |
-
<div className="border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| 308 |
<ErrorBoundary componentName="Sequence Panel">
|
| 309 |
<SequencePanel metadata={activeChainMetadata} />
|
| 310 |
</ErrorBoundary>
|
| 311 |
</div>
|
| 312 |
-
<div className="border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| 313 |
<ErrorBoundary componentName="Experimental Conditions">
|
| 314 |
<ExperimentalConditionsPanel metadata={activeChainMetadata} />
|
| 315 |
</ErrorBoundary>
|
| 316 |
</div>
|
| 317 |
{activeChainMetadata.nonpolymer_entities && activeChainMetadata.nonpolymer_entities.length > 0 && (
|
| 318 |
-
<div className="border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| 319 |
<ErrorBoundary componentName="Binders Panel">
|
| 320 |
<BindersPanel metadata={activeChainMetadata} />
|
| 321 |
</ErrorBoundary>
|
|
|
|
| 275 |
<main className="space-y-6">
|
| 276 |
{hasActiveChainSearch && activeChainMetadata ? (
|
| 277 |
<>
|
| 278 |
+
{/* First Row: Search + Chain Information (3:7 in landscape, stacked in portrait) */}
|
| 279 |
+
<div className="grid gap-6 items-stretch chain-detail-grid">
|
| 280 |
<section className="min-w-0 h-full border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| 281 |
<FilterPanel
|
| 282 |
filterOptions={filterOptions}
|
|
|
|
| 304 |
|
| 305 |
{/* Second Row: Sequence, Experimental Conditions, Binders (3 columns, equal height) */}
|
| 306 |
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
| 307 |
+
<div className="min-w-0 border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| 308 |
<ErrorBoundary componentName="Sequence Panel">
|
| 309 |
<SequencePanel metadata={activeChainMetadata} />
|
| 310 |
</ErrorBoundary>
|
| 311 |
</div>
|
| 312 |
+
<div className="min-w-0 border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| 313 |
<ErrorBoundary componentName="Experimental Conditions">
|
| 314 |
<ExperimentalConditionsPanel metadata={activeChainMetadata} />
|
| 315 |
</ErrorBoundary>
|
| 316 |
</div>
|
| 317 |
{activeChainMetadata.nonpolymer_entities && activeChainMetadata.nonpolymer_entities.length > 0 && (
|
| 318 |
+
<div className="min-w-0 border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| 319 |
<ErrorBoundary componentName="Binders Panel">
|
| 320 |
<BindersPanel metadata={activeChainMetadata} />
|
| 321 |
</ErrorBoundary>
|
frontend/src/components/datasets/protein/SequencePanel.tsx
CHANGED
|
@@ -111,11 +111,11 @@ const SequencePanel: React.FC<SequencePanelProps> = ({ metadata, isLoading }) =>
|
|
| 111 |
</div>
|
| 112 |
|
| 113 |
{/* Sequence Display */}
|
| 114 |
-
<div className="bg-slate-50 border border-slate-200 rounded-lg p-4 font-mono text-xs max-h-80 overflow-y-auto">
|
| 115 |
{displayLines.map((line, idx) => (
|
| 116 |
<div key={idx} className="flex mb-1 leading-relaxed">
|
| 117 |
-
<span className="text-gray-400 mr-4 min-w-[50px] text-right select-none">{idx * 60 + 1}</span>
|
| 118 |
-
<span className="text-gray-800 tracking-wide
|
| 119 |
</div>
|
| 120 |
))}
|
| 121 |
</div>
|
|
|
|
| 111 |
</div>
|
| 112 |
|
| 113 |
{/* Sequence Display */}
|
| 114 |
+
<div className="bg-slate-50 border border-slate-200 rounded-lg p-4 font-mono text-xs max-h-80 overflow-y-auto overflow-x-hidden">
|
| 115 |
{displayLines.map((line, idx) => (
|
| 116 |
<div key={idx} className="flex mb-1 leading-relaxed">
|
| 117 |
+
<span className="text-gray-400 mr-4 min-w-[50px] shrink-0 text-right select-none">{idx * 60 + 1}</span>
|
| 118 |
+
<span className="text-gray-800 tracking-wide wrap-anywhere">{line}</span>
|
| 119 |
</div>
|
| 120 |
))}
|
| 121 |
</div>
|
frontend/src/index.css
CHANGED
|
@@ -3063,3 +3063,22 @@ body {
|
|
| 3063 |
display: none;
|
| 3064 |
}
|
| 3065 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3063 |
display: none;
|
| 3064 |
}
|
| 3065 |
}
|
| 3066 |
+
|
| 3067 |
+
/* Chain detail top row: Chain Identifier + Chain metadata side by side
|
| 3068 |
+
(3:7) in landscape, stacked vertically in portrait (W < H). */
|
| 3069 |
+
.chain-detail-grid {
|
| 3070 |
+
grid-template-columns: minmax(0, 3fr) minmax(0, 7fr);
|
| 3071 |
+
}
|
| 3072 |
+
@media (orientation: portrait) {
|
| 3073 |
+
.chain-detail-grid {
|
| 3074 |
+
grid-template-columns: minmax(0, 1fr);
|
| 3075 |
+
}
|
| 3076 |
+
}
|
| 3077 |
+
|
| 3078 |
+
/* Let long unbroken content (amino-acid sequences, experimental details,
|
| 3079 |
+
SMILES, binder names) wrap instead of overflowing its card. */
|
| 3080 |
+
.wrap-anywhere {
|
| 3081 |
+
min-width: 0;
|
| 3082 |
+
overflow-wrap: anywhere;
|
| 3083 |
+
word-break: break-word;
|
| 3084 |
+
}
|