feat(editor): F4.5a the device sheet for PCs, notebooks, switches and modems
Measured first: the new oracle SheetRun types 77 texts into FILIUS' own settings panels and records what was marked red, what the device took and what the field showed afterwards. Java's two judgements disagree (red text taken, black text refused, any int as a modem port, a refused text dropped without a word); here one rule decides both (packages/doc fields.ts), a refused text stays marked in the field, and only what Java's model accepts reaches the document. - doc: settings.ts (name, label policy, addresses, IP forwarding, DHCP stopping the own DHCP server, LAN/WLAN with the wireless connection, switch SSID dropping its wireless hosts, retention time, cloud, modem port as its pair), sheet.ts (what the sheet shows) - fls: modemPair/modemPort in one place - editor: DeviceSheet beside the surface or below it on a phone, opened with the selection; every change one undo step - not adopted from Java: the name overwritten by the MAC address, the access point search depending on unrelated devices Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// The editor on a document: the palette, the surface, the tools of the design
|
||||
// mode (F4.4b). The device sheet and the simulation follow (F4.5, F4.6).
|
||||
// mode (F4.4b), the device sheet of the selected device (F4.5). The
|
||||
// simulation follows (F4.6).
|
||||
//
|
||||
// Every change goes through packages/doc's commands with this editor's own
|
||||
// origin, so that its undo takes back what this person did and nothing of
|
||||
@@ -13,6 +14,7 @@ import type { Point } from './canvas/viewport.js';
|
||||
import { ContextMenu, type MenuItem } from './design/ContextMenu.js';
|
||||
import { Palette, type PaletteKind } from './design/Palette.js';
|
||||
import { RouterDialog } from './design/RouterDialog.js';
|
||||
import { DeviceSheet } from './sheet/DeviceSheet.js';
|
||||
import { useTranslate } from './i18n/i18n.js';
|
||||
|
||||
/** The document's drawing, read anew after every change. */
|
||||
@@ -152,6 +154,8 @@ export function Editor({ doc }: { readonly doc: Y.Doc }) {
|
||||
return items;
|
||||
};
|
||||
|
||||
const selected = selection?.kind === 'device' ? view.nodes.find((node) => node.id === selection.id) : undefined;
|
||||
|
||||
// The keyboard: undo and redo, delete what is selected, Escape lets go.
|
||||
useEffect(() => {
|
||||
const key = (event: KeyboardEvent) => {
|
||||
@@ -186,58 +190,64 @@ export function Editor({ doc }: { readonly doc: Y.Doc }) {
|
||||
}}
|
||||
onDevice={onDevice}
|
||||
/>
|
||||
<div style={{ position: 'relative', flex: 1, minWidth: 0 }}>
|
||||
<Canvas
|
||||
ref={canvas}
|
||||
view={view}
|
||||
tool={cableTool ? 'cable' : 'select'}
|
||||
selection={selection}
|
||||
cableFrom={cableFrom}
|
||||
onSelect={setSelection}
|
||||
onMoveDevice={(id, x, y) => moveDevice(doc, id, x, y, origin)}
|
||||
onMoveEnd={() => manager?.stopCapturing()}
|
||||
onCableStart={setCableFrom}
|
||||
onCable={onCable}
|
||||
onMenu={(target, at) => {
|
||||
setSelection(target);
|
||||
setMenu({ target, at });
|
||||
}}
|
||||
/>
|
||||
<div role="toolbar" style={{ position: 'absolute', left: 12, top: 12, display: 'flex', gap: 6 }}>
|
||||
<button type="button" style={toolButton} disabled={!canUndo} onClick={() => manager?.undo()} aria-label={t('design.undo')} title={t('design.undo')}>
|
||||
↶
|
||||
</button>
|
||||
<button type="button" style={toolButton} disabled={!canRedo} onClick={() => manager?.redo()} aria-label={t('design.redo')} title={t('design.redo')}>
|
||||
↷
|
||||
</button>
|
||||
</div>
|
||||
{menu === null ? null : <ContextMenu at={menu.at} items={menuItems(menu.target)} onClose={() => setMenu(null)} />}
|
||||
{routerAt === null ? null : (
|
||||
<RouterDialog
|
||||
onChoose={(interfaces) => {
|
||||
place('router', routerAt, interfaces);
|
||||
setRouterAt(null);
|
||||
<div className="editor-work">
|
||||
<div className="editor-area">
|
||||
<Canvas
|
||||
ref={canvas}
|
||||
view={view}
|
||||
tool={cableTool ? 'cable' : 'select'}
|
||||
selection={selection}
|
||||
cableFrom={cableFrom}
|
||||
onSelect={setSelection}
|
||||
onMoveDevice={(id, x, y) => moveDevice(doc, id, x, y, origin)}
|
||||
onMoveEnd={() => manager?.stopCapturing()}
|
||||
onCableStart={setCableFrom}
|
||||
onCable={onCable}
|
||||
onMenu={(target, at) => {
|
||||
setSelection(target);
|
||||
setMenu({ target, at });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{message === null ? null : (
|
||||
<div
|
||||
role="alert"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
bottom: 72,
|
||||
transform: 'translateX(-50%)',
|
||||
padding: '10px 16px',
|
||||
borderRadius: 8,
|
||||
background: 'var(--danger-soft)',
|
||||
color: 'var(--danger-on-soft)',
|
||||
border: '1px solid var(--danger)',
|
||||
maxWidth: '80%',
|
||||
}}
|
||||
>
|
||||
{message}
|
||||
<div role="toolbar" style={{ position: 'absolute', left: 12, top: 12, display: 'flex', gap: 6 }}>
|
||||
<button type="button" style={toolButton} disabled={!canUndo} onClick={() => manager?.undo()} aria-label={t('design.undo')} title={t('design.undo')}>
|
||||
↶
|
||||
</button>
|
||||
<button type="button" style={toolButton} disabled={!canRedo} onClick={() => manager?.redo()} aria-label={t('design.redo')} title={t('design.redo')}>
|
||||
↷
|
||||
</button>
|
||||
</div>
|
||||
{menu === null ? null : <ContextMenu at={menu.at} items={menuItems(menu.target)} onClose={() => setMenu(null)} />}
|
||||
{routerAt === null ? null : (
|
||||
<RouterDialog
|
||||
onChoose={(interfaces) => {
|
||||
place('router', routerAt, interfaces);
|
||||
setRouterAt(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{message === null ? null : (
|
||||
<div
|
||||
role="alert"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
bottom: 72,
|
||||
transform: 'translateX(-50%)',
|
||||
padding: '10px 16px',
|
||||
borderRadius: 8,
|
||||
background: 'var(--danger-soft)',
|
||||
color: 'var(--danger-on-soft)',
|
||||
border: '1px solid var(--danger)',
|
||||
maxWidth: '80%',
|
||||
}}
|
||||
>
|
||||
{message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Java shows the settings of the device that is chosen; closing them lets go of it, as a click beside it does. */}
|
||||
{selected === undefined ? null : (
|
||||
<DeviceSheet doc={doc} id={selected.id} label={selected.label} origin={origin} onChanged={() => manager?.stopCapturing()} onMessage={setMessage} onClose={() => setSelection(null)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user