Tools + Elements panels: ragged pills → uniform grid tiles
Both panels shared the same flex-wrap pill layout where each pill sized to its content, producing a ragged, hard-to-scan right edge. Switch to a uniform CSS grid (auto-fill, minmax(68px, 1fr)) of icon-over-label tiles: - ToolsApp: ToolPill → ToolTile, PillGroup → GridSection - ElementeApp: PillButton → vertical tile (keeps hasMenu chevron, badge as corner pill, disabled state); PillGroup → grid section - Less chrome: borderless tiles on subtle --bg-item, accent border on hover - Dropdown tiles (Treppe/Stütze/Träger) keep their relative-positioned wrapper so PopupMenu still anchors correctly
This commit is contained in:
+34
-20
@@ -49,7 +49,8 @@ function ReferenzSelector({ value, onChange }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pill-Button — kompakt, Icon + Label horizontal, abgerundet
|
// Raster-Kachel — Icon oben, Label darunter, einheitliche Zellgroesse.
|
||||||
|
// hasMenu zeigt ein kleines Chevron neben dem Label (Rechtsklick = Untertypen).
|
||||||
function PillButton({ icon, label, hint, onClick, onContextMenu, disabled,
|
function PillButton({ icon, label, hint, onClick, onContextMenu, disabled,
|
||||||
hasMenu, badge }) {
|
hasMenu, badge }) {
|
||||||
return (
|
return (
|
||||||
@@ -59,49 +60,58 @@ function PillButton({ icon, label, hint, onClick, onContextMenu, disabled,
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
title={hint}
|
title={hint}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex', alignItems: 'center', gap: 6,
|
display: 'flex', flexDirection: 'column',
|
||||||
padding: '5px 10px 5px 8px',
|
alignItems: 'center', justifyContent: 'center', gap: 6,
|
||||||
background: 'var(--bg-input)',
|
padding: '10px 4px',
|
||||||
border: '1px solid var(--border-light)',
|
minHeight: 56,
|
||||||
borderRadius: 999,
|
background: 'var(--bg-item)',
|
||||||
|
border: '1px solid transparent',
|
||||||
|
borderRadius: 10,
|
||||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||||
opacity: disabled ? 0.4 : 1,
|
opacity: disabled ? 0.4 : 1,
|
||||||
transition: 'background 0.1s, border-color 0.1s',
|
transition: 'background 0.12s, border-color 0.12s',
|
||||||
fontSize: 11, fontWeight: 500,
|
fontSize: 10, fontWeight: 500,
|
||||||
color: 'var(--text-primary)',
|
color: 'var(--text-primary)',
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
|
appearance: 'none', WebkitAppearance: 'none',
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => { if (!disabled) {
|
onMouseEnter={(e) => { if (!disabled) {
|
||||||
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
||||||
e.currentTarget.style.borderColor = 'var(--accent)'
|
e.currentTarget.style.borderColor = 'var(--accent)'
|
||||||
}}}
|
}}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.background = 'var(--bg-input)'
|
e.currentTarget.style.background = 'var(--bg-item)'
|
||||||
e.currentTarget.style.borderColor = 'var(--border-light)'
|
e.currentTarget.style.borderColor = 'transparent'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon name={icon} size={14} style={{ color: 'var(--accent)' }} />
|
<Icon name={icon} size={19} style={{ color: 'var(--accent)', flexShrink: 0 }} />
|
||||||
<span>{label}</span>
|
<span style={{
|
||||||
|
display: 'flex', alignItems: 'center', gap: 2,
|
||||||
|
maxWidth: '100%', overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
||||||
|
}}>
|
||||||
|
{label}
|
||||||
{hasMenu && (
|
{hasMenu && (
|
||||||
<Icon name="expand_more" size={12}
|
<Icon name="expand_more" size={11}
|
||||||
style={{ color: 'var(--text-muted)', marginLeft: -2 }} />
|
style={{ color: 'var(--text-muted)', flexShrink: 0 }} />
|
||||||
)}
|
)}
|
||||||
|
</span>
|
||||||
{badge && (
|
{badge && (
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: 9, padding: '1px 5px', borderRadius: 8,
|
position: 'absolute', top: 4, right: 4,
|
||||||
|
fontSize: 8, padding: '0px 4px', borderRadius: 8,
|
||||||
background: 'var(--bg-section)', color: 'var(--text-muted)',
|
background: 'var(--bg-section)', color: 'var(--text-muted)',
|
||||||
marginLeft: 2,
|
|
||||||
}}>{badge}</span>
|
}}>{badge}</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertikale Kategorie-Gruppe mit Label + Pills, die wrappen
|
// Kategorie-Gruppe: Label + einheitliches Raster (auto-fill Spalten)
|
||||||
function PillGroup({ label, children }) {
|
function PillGroup({ label, children }) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: 9, color: 'var(--text-muted)',
|
fontSize: 9, color: 'var(--text-muted)',
|
||||||
letterSpacing: '0.08em', textTransform: 'uppercase',
|
letterSpacing: '0.08em', textTransform: 'uppercase',
|
||||||
@@ -109,7 +119,11 @@ function PillGroup({ label, children }) {
|
|||||||
}}>
|
}}>
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
|
<div style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(auto-fill, minmax(68px, 1fr))',
|
||||||
|
gap: 6,
|
||||||
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+27
-19
@@ -54,7 +54,7 @@ const TOOLS = {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function ToolPill({ icon, label, cmd, tip }) {
|
function ToolTile({ icon, label, cmd, tip }) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => runRhinoCommand(cmd)}
|
onClick={() => runRhinoCommand(cmd)}
|
||||||
@@ -64,32 +64,36 @@ function ToolPill({ icon, label, cmd, tip }) {
|
|||||||
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.borderColor = 'var(--border-light)'
|
e.currentTarget.style.borderColor = 'transparent'
|
||||||
e.currentTarget.style.background = 'var(--bg-input)'
|
e.currentTarget.style.background = 'var(--bg-item)'
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex', alignItems: 'center', gap: 6,
|
display: 'flex', flexDirection: 'column',
|
||||||
padding: '5px 10px 5px 8px',
|
alignItems: 'center', justifyContent: 'center', gap: 6,
|
||||||
background: 'var(--bg-input)',
|
padding: '10px 4px',
|
||||||
border: '1px solid var(--border-light)',
|
minHeight: 56,
|
||||||
borderRadius: 999,
|
background: 'var(--bg-item)',
|
||||||
|
border: '1px solid transparent',
|
||||||
|
borderRadius: 10,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
transition: 'background 0.1s, border-color 0.1s',
|
transition: 'background 0.12s, border-color 0.12s',
|
||||||
fontSize: 11, fontWeight: 500,
|
fontSize: 10, fontWeight: 500,
|
||||||
color: 'var(--text-primary)',
|
color: 'var(--text-primary)',
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
appearance: 'none', WebkitAppearance: 'none',
|
appearance: 'none', WebkitAppearance: 'none',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon name={icon} size={14} style={{ color: 'var(--accent)', flexShrink: 0 }} />
|
<Icon name={icon} size={19} style={{ color: 'var(--accent)', flexShrink: 0 }} />
|
||||||
<span>{label}</span>
|
<span style={{
|
||||||
|
maxWidth: '100%', overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
||||||
|
}}>{label}</span>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function PillGroup({ label, children }) {
|
function GridSection({ label, children }) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: 9, color: 'var(--text-muted)',
|
fontSize: 9, color: 'var(--text-muted)',
|
||||||
letterSpacing: '0.08em', textTransform: 'uppercase',
|
letterSpacing: '0.08em', textTransform: 'uppercase',
|
||||||
@@ -97,7 +101,11 @@ function PillGroup({ label, children }) {
|
|||||||
}}>
|
}}>
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
|
<div style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(auto-fill, minmax(68px, 1fr))',
|
||||||
|
gap: 6,
|
||||||
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,11 +130,11 @@ export default function WerkzeugeApp() {
|
|||||||
overflowY: 'auto', overflowX: 'hidden',
|
overflowY: 'auto', overflowX: 'hidden',
|
||||||
}}>
|
}}>
|
||||||
{groups.map(([title, items]) => (
|
{groups.map(([title, items]) => (
|
||||||
<PillGroup key={title} label={title}>
|
<GridSection key={title} label={title}>
|
||||||
{items.map(([icon, label, cmd, tip]) => (
|
{items.map(([icon, label, cmd, tip]) => (
|
||||||
<ToolPill key={cmd} icon={icon} label={label} cmd={cmd} tip={tip} />
|
<ToolTile key={cmd} icon={icon} label={label} cmd={cmd} tip={tip} />
|
||||||
))}
|
))}
|
||||||
</PillGroup>
|
</GridSection>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user