panel fixes 4
All checks were successful
Deploy Nyla Frontend to Integration / deploy (push) Successful in 1m26s

This commit is contained in:
ValueOn AG 2026-06-11 23:29:01 +02:00
parent 0ad9006b94
commit 27abfde833
10 changed files with 90 additions and 20 deletions

View file

@ -5,6 +5,18 @@
border-radius: 8px; border-radius: 8px;
background: var(--bg-primary, #fff); background: var(--bg-primary, #fff);
overflow: clip; overflow: clip;
display: flex;
flex-direction: column;
min-height: 0;
}
/* Card regions in bounded flex hosts (sidebar, PanelLayout pane): body fills and scrolls. */
.panel[data-variant="card"] .body:not(.bodyHidden) {
flex: 1;
min-height: 0;
overflow: auto;
display: flex;
flex-direction: column;
} }
/* --- Variant: table — fills available height, bounded scroll --- */ /* --- Variant: table — fills available height, bounded scroll --- */
@ -94,11 +106,14 @@
background: var(--bg-secondary, rgba(0, 0, 0, 0.02)); background: var(--bg-secondary, rgba(0, 0, 0, 0.02));
border-bottom: 1px solid var(--border-color, rgba(0, 0, 0, 0.08)); border-bottom: 1px solid var(--border-color, rgba(0, 0, 0, 0.08));
min-height: 40px; min-height: 40px;
position: relative;
} }
.headerCollapsible { .headerCollapsible {
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
/* Reserve space so action icons never overlap the collapse chevron */
padding-right: 30px;
} }
.headerCollapsible:hover { .headerCollapsible:hover {
@ -128,15 +143,20 @@
} }
.actions { .actions {
flex-shrink: 0; flex-shrink: 1;
min-width: 0;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;
margin-left: auto;
} }
/* Stable chevron position: fixed at the right edge of the header, same spot /* Collapse chevron: always rightmost, above action icons (never covered). */
whether collapsed or expanded. Icon swaps (down = open, right = collapsed). */
.chevron { .chevron {
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
flex-shrink: 0; flex-shrink: 0;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@ -145,6 +165,8 @@
height: 16px; height: 16px;
font-size: 12px; font-size: 12px;
color: var(--text-tertiary, #888); color: var(--text-tertiary, #888);
z-index: 2;
pointer-events: none;
} }
.body { .body {

View file

@ -75,12 +75,12 @@ export const Panel: FC<PanelProps> = ({
<span className={styles.title}>{title}</span> <span className={styles.title}>{title}</span>
{subtitle && <span className={styles.subtitle}>{subtitle}</span>} {subtitle && <span className={styles.subtitle}>{subtitle}</span>}
</div> </div>
{actions && <div className={styles.actions} onClick={(e) => e.stopPropagation()}>{actions}</div>}
{collapsible && ( {collapsible && (
<span className={styles.chevron} aria-hidden> <span className={styles.chevron} aria-hidden>
{collapsed ? <FaChevronRight /> : <FaChevronDown />} {collapsed ? <FaChevronRight /> : <FaChevronDown />}
</span> </span>
)} )}
{actions && <div className={styles.actions} onClick={(e) => e.stopPropagation()}>{actions}</div>}
</div> </div>
<div className={`${styles.body} ${collapsed ? styles.bodyHidden : ''}`}> <div className={`${styles.body} ${collapsed ? styles.bodyHidden : ''}`}>
{children} {children}

View file

@ -3,6 +3,8 @@
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
min-height: 0; min-height: 0;
height: 100%;
overflow: hidden;
gap: 8px; gap: 8px;
} }
@ -100,6 +102,9 @@
.tree { .tree {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1;
min-height: 0;
overflow-y: auto;
} }
.chatItem { .chatItem {

View file

@ -0,0 +1,22 @@
.sourcesTab {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
height: 100%;
overflow: hidden;
}
.treeHost {
flex: 1;
min-height: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.empty {
padding: 12px;
font-size: 12px;
color: #888;
}

View file

@ -20,6 +20,7 @@ import { createUdbSourcesProvider } from './UdbSourcesProvider';
import type { UdbBackendNode } from './UdbSourcesProvider'; import type { UdbBackendNode } from './UdbSourcesProvider';
import { DataSourceSettingsModal } from './DataSourceSettingsModal'; import { DataSourceSettingsModal } from './DataSourceSettingsModal';
import { useLanguage } from '../../providers/language/LanguageContext'; import { useLanguage } from '../../providers/language/LanguageContext';
import styles from './SourcesTab.module.css';
interface SourcesTabProps { interface SourcesTabProps {
context: UdbContext; context: UdbContext;
@ -84,15 +85,15 @@ const SourcesTab: React.FC<SourcesTabProps> = ({
if (!instanceId || !provider) { if (!instanceId || !provider) {
return ( return (
<div style={{ padding: 12, fontSize: 12, color: '#888' }}> <div className={styles.empty}>
{t('Keine Workspace-Instanz aktiv.')} {t('Keine Workspace-Instanz aktiv.')}
</div> </div>
); );
} }
return ( return (
<div style={{ height: '100%', display: 'flex', flexDirection: 'column', minHeight: 0 }}> <div className={styles.sourcesTab}>
<div style={{ flex: 1, overflow: 'auto', minHeight: 0 }}> <div className={styles.treeHost}>
<FormGeneratorTree <FormGeneratorTree
provider={provider} provider={provider}
ownership="own" ownership="own"

View file

@ -41,7 +41,9 @@
.tabContent { .tabContent {
flex: 1; flex: 1;
min-height: 0; min-height: 0;
overflow-y: auto; overflow: hidden;
display: flex;
flex-direction: column;
padding: 8px; padding: 8px;
} }

View file

@ -394,7 +394,7 @@ export const CommcoachSessionView: React.FC = () => {
collapsible: true, collapsible: true,
collapseKey: 'commcoach-session-udb', collapseKey: 'commcoach-session-udb',
content: ( content: (
<Panel variant="card" title={t('Datenquellen')} id="commcoach-session-data-sources"> <Panel variant="card" title={t('Datenquellen')} id="commcoach-session-data-sources" fill>
<UnifiedDataBar <UnifiedDataBar
context={_udbContext} context={_udbContext}
activeTab={udbTab} activeTab={udbTab}

View file

@ -1006,7 +1006,7 @@ export const TeamsbotSessionView: React.FC = () => {
collapsible: true, collapsible: true,
collapseKey: 'teamsbot-session-udb', collapseKey: 'teamsbot-session-udb',
content: ( content: (
<Panel variant="card" title={t('Datenquellen')} id="teamsbot-session-datasources"> <Panel variant="card" title={t('Datenquellen')} id="teamsbot-session-datasources" fill>
<UnifiedDataBar <UnifiedDataBar
context={_udbContext} context={_udbContext}
activeTab={udbTab} activeTab={udbTab}

View file

@ -67,6 +67,19 @@ export const WorkspaceContextSidebar: React.FC<WorkspaceContextSidebarProps> = (
aria-label="Kontext" aria-label="Kontext"
> >
<div className={styles.contextToolbar}> <div className={styles.contextToolbar}>
{allowCollapse && (
<button
type="button"
className={styles.contextCollapseBtn}
onClick={onToggleCollapsed}
title={isCollapsed ? 'Kontext ausklappen' : 'Kontext einklappen'}
aria-expanded={!isCollapsed}
>
{isCollapsed ? <FaChevronRight aria-hidden /> : <FaChevronLeft aria-hidden />}
</button>
)}
{!isCollapsed && (
<>
<button <button
type="button" type="button"
className={`${styles.contextToolBtn} ${activeTab === 'files' ? styles.contextToolBtnActive : ''}`} className={`${styles.contextToolBtn} ${activeTab === 'files' ? styles.contextToolBtnActive : ''}`}
@ -104,16 +117,7 @@ export const WorkspaceContextSidebar: React.FC<WorkspaceContextSidebarProps> = (
<FaEye aria-hidden /> <FaEye aria-hidden />
</button> </button>
<span className={styles.contextToolbarGrow} /> <span className={styles.contextToolbarGrow} />
{allowCollapse && ( </>
<button
type="button"
className={styles.contextCollapseBtn}
onClick={onToggleCollapsed}
title={isCollapsed ? 'Kontext ausklappen' : 'Kontext einklappen'}
aria-expanded={!isCollapsed}
>
{isCollapsed ? <FaChevronRight aria-hidden /> : <FaChevronLeft aria-hidden />}
</button>
)} )}
</div> </div>
{!isCollapsed && ( {!isCollapsed && (

View file

@ -162,6 +162,20 @@
width: 44px; width: 44px;
} }
.contextSidebarCollapsed .contextToolbar {
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 6px 4px;
gap: 6px;
border-bottom: none;
}
.contextSidebarCollapsed .contextCollapseBtn {
width: 32px;
height: 32px;
}
/* When hosted inside a PanelLayout pane, the sidebar fills the pane width so /* When hosted inside a PanelLayout pane, the sidebar fills the pane width so
the divider controls its size (instead of the fixed 320px). */ the divider controls its size (instead of the fixed 320px). */
.contextSidebarFill { .contextSidebarFill {