Zurueck zum Vectorworks-Look — Custom Pill-Chrome statt raw native HTML

User-Klaerung: erwartet wird der polierte Vectorworks-Style, nicht rohe
macOS-HTML-Form-Elemente. Vectorworks ist custom-gestylt um nativ
auszusehen, aber mit konsistentem Toolbar-Polish.

Zurueckgerollt:
- BarSelect: Pill-Container (bg-item, border, border-radius 999) +
  Custom-SVG-Caret aus --select-arrow. appearance:none. Icon roh links
  (Userpraferenz). joinedRight macht rechte Pill-Kante flach.
- BarButton: Pill-Container mit border + border-radius 999. active=true
  setzt accent-Background + bg-panel-Iconfarbe (klare Toggle-Anzeige).
  joinedLeft macht linke Kante flach fuer Verkettung.
- View-Toggle (Top/Front/Right/Iso/Persp): Segmented-Pill-Gruppe analog
  Vectorworks. Innere Kanten ohne Border, Aussenkanten gerundet.
  Active-Button: accent-fill + Bold.
- Custom-Massstab-Input: Pill-Chrome zurueck.

.native-control CSS-Klasse bleibt definiert (zukuenftige Verwendung), wird
aber in der Oberleiste nicht mehr referenziert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 22:28:17 +02:00
parent e19bbafe38
commit cf40c03602
+66 -39
View File
@@ -84,14 +84,10 @@ const pillSelect = {
fontSize: 10,
}
// BarSelect: Icon roh links + komplett system-natives <select>.
// Kein Background, kein Border, kein Border-Radius — WebKit/macOS rendert
// seinen eigenen Combobox-Look auch im Ruhezustand (gewölbtes Feld mit
// Caret-Indikator rechts). `colorScheme: dark` triggert die Dark-Variante.
//
// joinedRight bleibt als Prop drin aber als no-op — native Felder koennen
// nicht visuell mit unseren Custom-Buttons verkettet werden.
function BarSelect({ icon, value, onChange, title, disabled, width, children }) {
// BarSelect: Icon roh links + custom-pill <select>. Vectorworks-Stil —
// dunkler Pill-Container, Caret rechts, joinedRight macht die rechte Kante
// flach fuer die Verkettung mit BarButton.
function BarSelect({ icon, value, onChange, title, disabled, width, children, joinedRight }) {
return (
<div title={title} style={{
display: 'inline-flex', alignItems: 'center', gap: 5,
@@ -102,38 +98,53 @@ function BarSelect({ icon, value, onChange, title, disabled, width, children })
style={{ color: 'var(--text-muted)', flexShrink: 0 }} />
)}
<select
className="native-control"
value={value || ''}
disabled={disabled}
onChange={(e) => onChange(e.target.value)}
style={{
height: BAR_H, width,
fontSize: 11,
colorScheme: 'dark',
flexShrink: 0,
background: 'var(--bg-item)',
color: 'var(--text-primary)',
border: '1px solid var(--border)',
borderTopLeftRadius: 999, borderBottomLeftRadius: 999,
borderTopRightRadius: joinedRight ? 0 : 999,
borderBottomRightRadius: joinedRight ? 0 : 999,
borderRight: joinedRight ? 'none' : '1px solid var(--border)',
padding: '0 26px 0 12px',
fontSize: 11, fontFamily: 'var(--font)',
appearance: 'none', WebkitAppearance: 'none',
backgroundImage: 'var(--select-arrow)',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'right 10px center',
cursor: disabled ? 'not-allowed' : 'pointer',
flexShrink: 0, outline: 'none',
letterSpacing: 0,
}}
>{children}</select>
</div>
)
}
// BarButton: bare <button class="native-control"> — macOS Push-Button
// Look (gewölbt, rounded). active=true setzt is-active fuer Bold-Text
// und Icon-Tint.
function BarButton({ icon, onClick, title, disabled, active }) {
// BarButton: pill-foermiger Icon-Button im selben Stil wie BarSelect.
// joinedLeft = linke Kante flach (dockt rechts an einen BarSelect-joinedRight).
function BarButton({ icon, onClick, title, disabled, active, joinedLeft }) {
return (
<button
className={`native-control${active ? ' is-active' : ''}`}
onClick={onClick} disabled={disabled} title={title}
<button onClick={onClick} disabled={disabled} title={title}
style={{
height: BAR_H,
fontSize: 11,
colorScheme: 'dark',
flexShrink: 0,
height: BAR_H, width: BAR_H,
background: active ? 'var(--accent)' : 'var(--bg-item)',
border: '1px solid var(--border)',
borderTopLeftRadius: joinedLeft ? 0 : 999,
borderBottomLeftRadius: joinedLeft ? 0 : 999,
borderTopRightRadius: 999, borderBottomRightRadius: 999,
borderLeft: joinedLeft ? 'none' : '1px solid var(--border)',
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
cursor: disabled ? 'not-allowed' : 'pointer',
opacity: disabled ? 0.5 : 1, flexShrink: 0,
padding: 0,
}}>
<Icon name={icon} size={13}
style={{ color: active ? 'var(--accent)' : undefined,
verticalAlign: 'middle' }} />
style={{ color: active ? 'var(--bg-panel)' : 'var(--text-muted)' }} />
</button>
)
}
@@ -317,25 +328,38 @@ export default function OberleisteApp() {
</button>
<div style={sep} />
{/* ====== VIEW (Top/Front/Right/Iso/Persp + Kamera) ======
Individuelle native Push-Buttons. Active-State via is-active
(Bold + Icon-Tint). Kein Pill-Container. */}
{VIEWS.map(v => (
Segmented-Pill-Gruppe analog Vectorworks. Active = accent fill. */}
<div style={{ display: 'inline-flex', flexShrink: 0 }}>
{VIEWS.map((v, idx) => {
const isFirst = idx === 0
const isLast = idx === VIEWS.length - 1
const isActive = matchView(v.value)
return (
<button
key={v.value}
className={`native-control${matchView(v.value) ? ' is-active' : ''}`}
onClick={() => setView(v.value)}
title={`Ansicht ${v.label}`}
style={{
height: BAR_H, fontSize: 11,
colorScheme: 'dark', flexShrink: 0,
height: BAR_H, padding: '0 10px',
background: isActive ? 'var(--accent)' : 'var(--bg-item)',
color: isActive ? 'var(--bg-panel)' : 'var(--text-primary)',
border: '1px solid var(--border)',
borderLeft: isFirst ? '1px solid var(--border)' : 'none',
borderTopLeftRadius: isFirst ? 999 : 0,
borderBottomLeftRadius: isFirst ? 999 : 0,
borderTopRightRadius: isLast ? 999 : 0,
borderBottomRightRadius: isLast ? 999 : 0,
display: 'inline-flex', alignItems: 'center', gap: 4,
fontSize: 10, fontWeight: isActive ? 600 : 500,
cursor: 'pointer', flexShrink: 0,
}}
>
<Icon name={v.icon} size={13}
style={{ verticalAlign: 'middle', marginRight: 4,
color: matchView(v.value) ? 'var(--accent)' : undefined }} />
{v.label}
<Icon name={v.icon} size={13} />
<span>{v.label}</span>
</button>
))}
)
})}
</div>
<BarButton icon="videocam" onClick={() => openKameraPanel()}
title="Kamera-Einstellungen (Position, Target, Linse, Presets)" />
@@ -393,7 +417,6 @@ export default function OberleisteApp() {
{customMode ? (
<input
ref={customInputRef}
className="native-control"
disabled={isPerspective}
type="text" placeholder="1:N"
value={draft}
@@ -405,9 +428,13 @@ export default function OberleisteApp() {
onBlur={applyDraft}
style={{
height: BAR_H, width: 100,
fontSize: 11,
background: 'var(--bg-item)',
color: 'var(--text-primary)',
border: '1px solid var(--border)',
borderRadius: 999,
padding: '0 12px', fontSize: 11,
fontFamily: 'DM Mono, monospace',
colorScheme: 'dark',
outline: 'none',
}}
title="Massstab eingeben (Enter = uebernehmen, Esc = abbrechen)"
/>