cd87a0b7f4
- Tile content back to horizontal (icon beside label, left-aligned) while keeping the aligned grid columns — per user choice 'Raster + horizontal' - Wider 'both' column (86px) to fit icon+label in one row, single-row height - Display-mode dropdown moved to the left (justify-content flex-start) - icon-only and text-only modes stay centered; hasMenu chevron inline next to label (both/text) or as corner indicator (icon-only)
182 lines
7.1 KiB
React
182 lines
7.1 KiB
React
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
// Copyright (C) 2026 Karim Gabriele Varano
|
|
import { useEffect, useState } from 'react'
|
|
import Icon from './components/Icon'
|
|
import { notifyReady, runRhinoCommand } from './lib/rhinoBridge'
|
|
|
|
// Anzeige-Modus der Kacheln, geteilt mit Elemente-Panel via localStorage.
|
|
const TILE_MODE_KEY = 'dossier_tile_mode' // 'both' | 'icon' | 'text'
|
|
function readTileMode() {
|
|
try { return localStorage.getItem(TILE_MODE_KEY) || 'both' } catch { return 'both' }
|
|
}
|
|
function writeTileMode(m) {
|
|
try { localStorage.setItem(TILE_MODE_KEY, m) } catch { /* WebView ohne Storage */ }
|
|
}
|
|
const TILE_MIN_COL = { icon: 40, text: 64, both: 86 }
|
|
const TILE_MIN_H = { icon: 32, text: 30, both: 32 }
|
|
|
|
// Tool-Definitionen: [icon, label, rhino-command, tooltip]
|
|
// Material-Symbol-Namen siehe https://fonts.google.com/icons
|
|
const TOOLS = {
|
|
'2D Zeichnen': [
|
|
['horizontal_rule', 'Line', '_Line', 'Linie zwischen zwei Punkten'],
|
|
['polyline', 'Polyline', '_Polyline', 'Polylinie'],
|
|
['rectangle', 'Rect', '_Rectangle', 'Rechteck'],
|
|
['radio_button_unchecked', 'Circle', '_Circle', 'Kreis'],
|
|
['network_intelligence', 'Arc', '_Arc', 'Bogen'],
|
|
['gesture', 'Curve', '_Curve', 'Freie Kurve (Spline)'],
|
|
['text_fields', 'Text', '_Text', 'Text'],
|
|
['grid_view', 'Hatch', '_Hatch', 'Schraffur'],
|
|
['straighten', 'Dim', '_Dim', 'Linearbemassung'],
|
|
],
|
|
'2D Editieren': [
|
|
['open_with', 'Move', '_Move', 'Verschieben'],
|
|
['content_copy', 'Copy', '_Copy', 'Kopieren'],
|
|
['rotate_right', 'Rotate', '_Rotate', 'Drehen'],
|
|
['aspect_ratio', 'Scale', '_Scale', 'Skalieren'],
|
|
['flip', 'Mirror', '_Mirror', 'Spiegeln'],
|
|
['padding', 'Offset', '_Offset', 'Parallelversatz'],
|
|
['content_cut', 'Trim', '_Trim', 'Stutzen'],
|
|
['swipe_right_alt', 'Extend', '_Extend', 'Verlängern'],
|
|
['link', 'Join', '_Join', 'Verbinden'],
|
|
['scatter_plot', 'Explode', '_Explode', 'Auflösen'],
|
|
['rounded_corner', 'Fillet', '_Fillet', 'Verrunden (Ecke abrunden)'],
|
|
['apps', 'Array', '_ArrayPolar','Polar-Array'],
|
|
],
|
|
'3D Modellieren': [
|
|
['vertical_align_top','Extrude', '_ExtrudeCrv', 'Kurve zu 3D extrudieren'],
|
|
['square', 'Box', '_Box', 'Quader'],
|
|
['join_inner', 'Union', '_BooleanUnion', 'Boolean-Vereinigung'],
|
|
['remove', 'Diff', '_BooleanDifference', 'Boolean-Differenz'],
|
|
['gradient', 'Intersect','_BooleanIntersection','Boolean-Schnittmenge'],
|
|
['roofing', 'Cap', '_Cap', 'Planare Loecher schliessen'],
|
|
['cut', 'Section', '_Section', 'Schnittlinien erzeugen'],
|
|
['unfold_more', 'Loft', '_Loft', 'Loft (Kurven verbinden)'],
|
|
],
|
|
'Auswahl': [
|
|
['add_link', 'Chain', '_SelChain', 'Tangentiale Kurvenkette wählen'],
|
|
['filter_alt', 'Dup', '_SelDup', 'Doppelte Objekte wählen'],
|
|
['loop', 'Closed', '_SelClosedCrv', 'Geschlossene Kurven wählen'],
|
|
['compare_arrows', 'Invert', '_Invert', 'Auswahl invertieren'],
|
|
['select_all', 'All', '_SelAll', 'Alle auswählen'],
|
|
['deselect', 'None', '_SelNone', 'Auswahl aufheben'],
|
|
],
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function ToolTile({ icon, label, cmd, tip, mode }) {
|
|
const showIcon = mode !== 'text'
|
|
const showLabel = mode !== 'icon'
|
|
return (
|
|
<button
|
|
onClick={() => runRhinoCommand(cmd)}
|
|
title={`${tip} (${cmd})`}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.borderColor = 'var(--accent)'
|
|
e.currentTarget.style.background = 'var(--bg-item-hover)'
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.borderColor = 'var(--border-light)'
|
|
e.currentTarget.style.background = 'var(--bg-input)'
|
|
}}
|
|
style={{
|
|
display: 'flex', flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: mode === 'both' ? 'flex-start' : 'center',
|
|
gap: showIcon && showLabel ? 7 : 0,
|
|
padding: mode === 'icon' ? '6px' : '5px 11px',
|
|
minHeight: TILE_MIN_H[mode],
|
|
background: 'var(--bg-input)',
|
|
border: '1px solid var(--border-light)',
|
|
borderRadius: 999,
|
|
cursor: 'pointer',
|
|
transition: 'background 0.12s, border-color 0.12s',
|
|
fontSize: 11, fontWeight: 500,
|
|
color: 'var(--text-primary)',
|
|
appearance: 'none', WebkitAppearance: 'none',
|
|
}}
|
|
>
|
|
{showIcon && (
|
|
<Icon name={icon} size={16}
|
|
style={{ color: 'var(--accent)', flexShrink: 0 }} />
|
|
)}
|
|
{showLabel && (
|
|
<span style={{
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
|
}}>{label}</span>
|
|
)}
|
|
</button>
|
|
)
|
|
}
|
|
|
|
function GridSection({ label, mode, children }) {
|
|
return (
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
|
|
<span style={{
|
|
fontSize: 9, color: 'var(--text-muted)',
|
|
letterSpacing: '0.08em', textTransform: 'uppercase',
|
|
fontWeight: 600,
|
|
}}>
|
|
{label}
|
|
</span>
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: `repeat(auto-fill, minmax(${TILE_MIN_COL[mode]}px, 1fr))`,
|
|
gap: 5,
|
|
}}>
|
|
{children}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function TileModeDropdown({ mode, onChange }) {
|
|
return (
|
|
<select
|
|
value={mode}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
title="Anzeige: Symbol / Text / Symbol + Text"
|
|
style={{ fontSize: 10, padding: '3px 6px', maxWidth: 130 }}
|
|
>
|
|
<option value="icon">Symbol</option>
|
|
<option value="text">Text</option>
|
|
<option value="both">Symbol + Text</option>
|
|
</select>
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export default function WerkzeugeApp() {
|
|
const [mode, setMode] = useState(readTileMode)
|
|
useEffect(() => { notifyReady() }, [])
|
|
|
|
const changeMode = (m) => { setMode(m); writeTileMode(m) }
|
|
const groups = Object.entries(TOOLS)
|
|
|
|
return (
|
|
<div style={{
|
|
width: '100%', height: '100%',
|
|
display: 'flex', flexDirection: 'column', gap: 10,
|
|
padding: 10,
|
|
fontFamily: 'var(--font)', color: 'var(--text-primary)',
|
|
background: 'var(--bg-base)',
|
|
boxSizing: 'border-box',
|
|
overflowY: 'auto', overflowX: 'hidden',
|
|
}}>
|
|
<div style={{ display: 'flex', justifyContent: 'flex-start' }}>
|
|
<TileModeDropdown mode={mode} onChange={changeMode} />
|
|
</div>
|
|
{groups.map(([title, items]) => (
|
|
<GridSection key={title} label={title} mode={mode}>
|
|
{items.map(([icon, label, cmd, tip]) => (
|
|
<ToolTile key={cmd} icon={icon} label={label} cmd={cmd} tip={tip} mode={mode} />
|
|
))}
|
|
</GridSection>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|