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
+74 -47
View File
@@ -84,14 +84,10 @@ const pillSelect = {
fontSize: 10, fontSize: 10,
} }
// BarSelect: Icon roh links + komplett system-natives <select>. // BarSelect: Icon roh links + custom-pill <select>. Vectorworks-Stil —
// Kein Background, kein Border, kein Border-Radius — WebKit/macOS rendert // dunkler Pill-Container, Caret rechts, joinedRight macht die rechte Kante
// seinen eigenen Combobox-Look auch im Ruhezustand (gewölbtes Feld mit // flach fuer die Verkettung mit BarButton.
// Caret-Indikator rechts). `colorScheme: dark` triggert die Dark-Variante. function BarSelect({ icon, value, onChange, title, disabled, width, children, joinedRight }) {
//
// 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 }) {
return ( return (
<div title={title} style={{ <div title={title} style={{
display: 'inline-flex', alignItems: 'center', gap: 5, 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 }} /> style={{ color: 'var(--text-muted)', flexShrink: 0 }} />
)} )}
<select <select
className="native-control"
value={value || ''} value={value || ''}
disabled={disabled} disabled={disabled}
onChange={(e) => onChange(e.target.value)} onChange={(e) => onChange(e.target.value)}
style={{ style={{
height: BAR_H, width, height: BAR_H, width,
fontSize: 11, background: 'var(--bg-item)',
colorScheme: 'dark', color: 'var(--text-primary)',
flexShrink: 0, 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> >{children}</select>
</div> </div>
) )
} }
// BarButton: bare <button class="native-control"> — macOS Push-Button // BarButton: pill-foermiger Icon-Button im selben Stil wie BarSelect.
// Look (gewölbt, rounded). active=true setzt is-active fuer Bold-Text // joinedLeft = linke Kante flach (dockt rechts an einen BarSelect-joinedRight).
// und Icon-Tint. function BarButton({ icon, onClick, title, disabled, active, joinedLeft }) {
function BarButton({ icon, onClick, title, disabled, active }) {
return ( return (
<button <button onClick={onClick} disabled={disabled} title={title}
className={`native-control${active ? ' is-active' : ''}`}
onClick={onClick} disabled={disabled} title={title}
style={{ style={{
height: BAR_H, height: BAR_H, width: BAR_H,
fontSize: 11, background: active ? 'var(--accent)' : 'var(--bg-item)',
colorScheme: 'dark', border: '1px solid var(--border)',
flexShrink: 0, 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} <Icon name={icon} size={13}
style={{ color: active ? 'var(--accent)' : undefined, style={{ color: active ? 'var(--bg-panel)' : 'var(--text-muted)' }} />
verticalAlign: 'middle' }} />
</button> </button>
) )
} }
@@ -317,25 +328,38 @@ export default function OberleisteApp() {
</button> </button>
<div style={sep} /> <div style={sep} />
{/* ====== VIEW (Top/Front/Right/Iso/Persp + Kamera) ====== {/* ====== VIEW (Top/Front/Right/Iso/Persp + Kamera) ======
Individuelle native Push-Buttons. Active-State via is-active Segmented-Pill-Gruppe analog Vectorworks. Active = accent fill. */}
(Bold + Icon-Tint). Kein Pill-Container. */} <div style={{ display: 'inline-flex', flexShrink: 0 }}>
{VIEWS.map(v => ( {VIEWS.map((v, idx) => {
<button const isFirst = idx === 0
key={v.value} const isLast = idx === VIEWS.length - 1
className={`native-control${matchView(v.value) ? ' is-active' : ''}`} const isActive = matchView(v.value)
onClick={() => setView(v.value)} return (
title={`Ansicht ${v.label}`} <button
style={{ key={v.value}
height: BAR_H, fontSize: 11, onClick={() => setView(v.value)}
colorScheme: 'dark', flexShrink: 0, title={`Ansicht ${v.label}`}
}} style={{
> height: BAR_H, padding: '0 10px',
<Icon name={v.icon} size={13} background: isActive ? 'var(--accent)' : 'var(--bg-item)',
style={{ verticalAlign: 'middle', marginRight: 4, color: isActive ? 'var(--bg-panel)' : 'var(--text-primary)',
color: matchView(v.value) ? 'var(--accent)' : undefined }} /> border: '1px solid var(--border)',
{v.label} borderLeft: isFirst ? '1px solid var(--border)' : 'none',
</button> 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} />
<span>{v.label}</span>
</button>
)
})}
</div>
<BarButton icon="videocam" onClick={() => openKameraPanel()} <BarButton icon="videocam" onClick={() => openKameraPanel()}
title="Kamera-Einstellungen (Position, Target, Linse, Presets)" /> title="Kamera-Einstellungen (Position, Target, Linse, Presets)" />
@@ -393,7 +417,6 @@ export default function OberleisteApp() {
{customMode ? ( {customMode ? (
<input <input
ref={customInputRef} ref={customInputRef}
className="native-control"
disabled={isPerspective} disabled={isPerspective}
type="text" placeholder="1:N" type="text" placeholder="1:N"
value={draft} value={draft}
@@ -405,9 +428,13 @@ export default function OberleisteApp() {
onBlur={applyDraft} onBlur={applyDraft}
style={{ style={{
height: BAR_H, width: 100, 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', fontFamily: 'DM Mono, monospace',
colorScheme: 'dark', outline: 'none',
}} }}
title="Massstab eingeben (Enter = uebernehmen, Esc = abbrechen)" title="Massstab eingeben (Enter = uebernehmen, Esc = abbrechen)"
/> />